Validating django rest api get request using traditional forms class - python-3.x

I am trying to validate a DRF get request using django form as follows,
The view of django rest api
#csrf_exempt
#api_view(['GET', 'POST'])
def pkg_list(request):
if request.method == 'GET':
frm=ThisForm(request.GET)
if frm.is_valid:
print("form ok")
print(frm.cleaned_data)
else:
print("invalid")
mydata=[{"email": request.GET['reseller']}]
results=ResellerListPackages(mydata,many=True).data
return Response(results)
The view class is form is as follows,
class ThisForm(forms.Form):
reseller=forms.EmailField(max_length=255)
def clean(self):
self.cleaned_data = super().clean()
print(self.cleaned_data)
return self.cleaned_data
The form validation seems working fine , but the frm.cleaned_data is not found with the following error,
print(frm.cleaned_data)
AttributeError: 'ThisForm' object has no attribute 'cleaned_data'
Can some one point to me the correct direction. It is the first time using the DRF

Change
frm.is_valid
to
frm.is_valid()
Forms only get a cleaned_data attribute when is_valid() has been called, and you haven't called it on this new, second instance.
but in your case, you are not calling the is_valid() method.

Related

What is 'obj' while creating custom list_display in the Django ModelAdmin

While going through the Django docs for a custom list_display (displaying fields in the admin, other than the fields present in the model), I came through the below code:
class PersonAdmin(admin.ModelAdmin):
list_display = ('upper_case_name',)
#admin.display(description='Name')
def upper_case_name(self, obj):
return ("%s %s" % (obj.first_name, obj.last_name)).upper()
The PersonAdmin class is the self in the method upper_case_name, but I wonder what is the obj here? And how does this object get passed into this method?

Automatic method delegation in Python

I have a rather contrived code here :
backend_data = {
"admins": ["Leo", "Martin", "Thomas", "Katrin"],
"members": [
"Leo",
"Martin",
"Thomas",
"Katrin",
"Subhayan",
"Clemens",
"Thoralf"
],
"juniors": ["Orianne", "Antonia", "Sarah"]
}
class Backend:
def __init__(self, data):
self.backend_data = data
def get_all_admins(self):
return self.backend_data.get("admins")
def get_all_members(self):
return self.backend_data.get("members")
def get_all_juniors(self):
return self.backend_data.get("juniors")
class BackendAdaptor:
# Does some conversion and validation
def __init__(self, backend):
self.backend = backend
def get_all_admins(self):
return (admin for admin in self.backend.get_all_admins())
def get_all_members(self):
return (member for member in self.backend.get_all_members() if member not in self.backend.get_all_admins())
def get_all_juniors(self):
return (junior for junior in self.backend.get_all_juniors())
if __name__ == "__main__":
backend = Backend(data=backend_data)
adaptor = BackendAdaptor(backend=backend)
print(f"All admins are : {list(adaptor.get_all_admins())}")
print(f"All members are : {list(adaptor.get_all_members())}")
print(f"All juniors are : {list(adaptor.get_all_juniors())}")
So the BackendAdaptor class basically would be used to do some validation and conversion of the data that we get from the Backend .
The client should only be asked to interact with the API of the BackendAdaptor which is exactly similar to that of Backend . The adaptor class sits in middle and gets data from Backend does some validation if required and the gives back the data to client.
The issue is that the validation on the data that is getting returned from the Backend is not done for every method(For ex: there is validation done on get_all_members but not on get_all_admins and also not on get_all_juniors). The method just gives back a generator on whatever data it gets from Backend.
As is the case now i still have to implement a one liner methods for them .
Is there a way in Python to avoid this ? I am thinking in lines of magic methods like __getattribute__ ? But i have no idea on how to do this for methods.
So the best case scenario is this:
I implement the methods for which i know that i have to do some validation on Backend data
For the rest of the methods it is automatically delegated to Backend and then i just return a generator from what i get back
Any help would be greatly appreciated.
You can implement __getattr__. It is only called if a non-existing attribute is accessed. This will return some generic function with the desired functionality.
class BackendAdaptor:
def __init__(self, backend):
self.backend = backend
def __getattr__(self, name):
if not hasattr(self.backend, name):
raise AttributeError(f"'{name}' not in backend.")
return lambda: (i for i in getattr(self.backend, name)())
def get_all_members(self):
return (member for member in self.backend.get_all_members() if member not in self.backend.get_all_admins())

AttributeError: 'NoneType' object has no attribute on insert from form

I am trying to add form data to mongodb using flask-pymongo insert. I get the following error, and I cant see what I am doing wrong
I've tried following tutorials on youtube but while my code seems similar i still get error "AttributeError: 'NoneType' object has no attribute".
#users.route('/login', methods=['POST', 'GET'])
def login():
print("your in the user route login")
if request.method == 'POST':
print("you are in post route")
if request.form['username'] and request.form['password'] == "":
print("no data was collected")
else:
users_login = mongo.db.fred
users_login.insert({'user': request.form['username'],\
'password': request.form['password']})
print("submitted to db")
return render_template('login.html')
return render_template('login.html')
was hoping form fields collected with request.form would be posted to db but just get error
I've solved this by starting overagain and by using user_collection = mongo.db.user which is the targeted collection. so does this mean you cant specify a variable for mongo.db.user, and you need to use user_collection ? T

DRF request is not defined for getting current user id

So What I've been trying to do is to have my API view only return objects that have their attributes post_user to the current id of the logged in user. These post_user attributes are populated as whenever I post it populates the variable with the current user's id through my serializer.
However, I am not successful as it says request is not defined. I just want to get the current user's id so that I can use it to filter my object returns
views.py
# To retrieve and list all posts with DRF
class ListPosts(generics.ListCreateAPIView):
queryset = Posts.objects.get(post_user=request.user.id)
serializer_class = PostsSerializer
permission_classes = (permissions.IsAuthenticated,)
serializers.py
# serializer for posts to be taken
class PostsSerializer(serializers.ModelSerializer):
class Meta:
model = Posts
fields = ('id','post_title','post_content',)
def create(self, validated_data):
posts = Posts.objects.create(
post_title=validated_data['post_title'],
post_content=validated_data['post_content'],
# gets the id of the current user
post_user=self.context['request'].user.id,
)
posts.save()
return posts
error is in line
queryset = Posts.objects.get(post_user=request.user.id)
here request is not define at class declaration time. Solution is you can override the get_queryset method.
class ListPosts(generics.ListCreateAPIView):
queryset = Posts.objects.all()
serializer_class = PostsSerializer
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self, *args, **kwargs):
return Posts.objects.filter(post_user=self.request.user)
Inherit CreateModelMixin's features inside PostsSerializer and try to define your create() method like def create(request, *args, **kwargs).
Finally, you can try to get user id using request.user.id.
For a better documentation, you can check https://www.django-rest-framework.org/api-guide/generic-views/.
Also check what are Mixins and why do we use it (if you do not know).
For a little and brief definition, Mixins are just class with methods that can be mostly inherited and used by our views.
If you have any doubt, please comment.

Unexpected AssertionError: single test not using logged in user from previous step

I am following the tutorial by http://www.patricksoftwareblog.com/flask-tutorial/, which I believe is based on https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world. Great stuff for a beginner.
I am getting different results when testing my code through frontend manually (which works fine) v.s. through pytest.
My test tries to show the "groups" endpoint which requires a login (standard #login_required decorator).
I initially test the user getting a login page ("Knock knock") when trying to get the endpoint without a login. This works manually and through pytest.
I login a user. If I inspect the response from the login I can clearly see a "Welcome back Pete!" success message.
My second assert receives a response from URL /login?next=%2Fgroups indicating the /groups endpoint is called without a login/authentication preceding it and the assert fails. Testing this manually works as expected. Why is that single test not using the same user/session combination in the next step(s)?
Test with the problem is the first snippet below:
def test_groups(app):
assert b'Knock knock' in get(app, "/groups").data
login(app, "pete#testmail.com", "pete123")
assert b'Test group 1' in get(app, "/groups").data
My "get" function for reference:
def get(app, endpoint: str):
return app.test_client().get(endpoint, follow_redirects=True)
My "login" function for reference:
def login(app, email="testuser#testmail.com", password="testing"):
return app.test_client().post('/login', data=dict(email=email, password=password), follow_redirects=True)
The app (from a conftest fixture imported in the test module by #pytest.mark.usefixtures('app')) for reference:
#pytest.fixture
def app():
"""An application for the tests."""
_app = create_app(DevConfig)
ctx = _app.test_request_context()
ctx.push()
yield _app
ctx.pop()
The login route for reference:
#app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm(request.form)
if request.method == 'POST':
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user is not None and user.is_correct_password(form.password.data):
user.authenticated = True
user.last_login = user.current_login
user.current_login = datetime.now()
user.insert_user()
login_user(user)
flash(f'Welcome back {user.name}!', 'success')
return redirect(url_for('our_awesome_group.index'))
else:
flash('Incorrect credentials! Did you already register?', 'error')
else:
flash_errors(form)
return render_template('login.html', form=form)
The groups route for reference:
#app.route('/groups')
#login_required
def groups():
groups_and_users = dict()
my_group_uuids = Membership.list_groups_per_user(current_user)
my_groups = [Group.query.filter_by(uuid=group).first() for group in my_group_uuids]
for group in my_groups:
user_uuids_in_group = Membership.list_users_per_group(group)
users_in_group = [User.query.filter_by(uuid=user).first() for user in user_uuids_in_group]
groups_and_users[group] = users_in_group
return render_template('groups.html', groups_and_users=groups_and_users)
Im going to sum up the comments I made that gave the answer on how to solve this issue.
When creating a test app using Pytest and Flask there are a few different ways to go about it.
The suggested way to create a test client with proper app context is to use something like:
#pytest.fixture
def client():
""" Creates the app from testconfig, activates test client and context then makes the db and allows the test client
to be used """
app = create_app(TestConfig)
client = app.test_client()
ctx = app.app_context()
ctx.push()
db.create_all()
yield client
db.session.close()
db.drop_all()
ctx.pop()
That creates the client while pushing the app context so you can register things like your database and create the tables to the test client.
The second way is show in OP's question where use app.test_request context
#pytest.fixture
def app():
"""An application for the tests."""
_app = create_app(DevConfig)
ctx = _app.test_request_context()
ctx.push()
yield _app
ctx.pop()
and then create the test client in another pytest fixture
#pytest.fixture
def client(app):
return app.test_client()
Creating a test client allows you to use various testing features and gives access to flask requests with the proper app context.

Resources