Does django-rest-swagger not work well with modelserializers? - python

I've been going off of the documentation on the django-rest-swagger github page, more specifically the part called "How it works". It shows that you can define your own parameters for your rest api, and have those parameters show up in your swagger doc page.
The commenting example is something like:
"""
This text is the description for this API
param1 -- A first parameter
param2 -- A second parameter
"""
I can get this to work, but my issue is how to specify if the variable is required, its parameter type, and its data type. The github page shows an example image of how your swagger doc could look, and they have the information I just mentioned. But when I comment my custom parameters like the example shows, my parameters just show as parameter type: "query", data type: is blank, and it doesn't show "required".
The closest thing I have found to an answer was in this stackoverflow question. It seems like an answer provider is saying that django-rest-swagger generates its documentation by automatically inspecting your serializers (which is fine), and that modelserializers won't contain enough information for django-rest-swagger to properly derive the criteria I mentioned above. I get that it can't figure out this criteria but there must be some way for me to manually specify it then.
Am I correct that django-rest-swagger would only display what I want if I rewrote my modelserializers as just serializers? Is there no way for me to manually tell django-rest-swagger what a parameter's parameter type and data type should be, and if it's required?
I know I must be missing something here. I use class-based views and modelserializers that are almost identical to the examples in the django-rest-framework tutorials. It seems entirely possible that I'm just missing an understanding of "parameter types" in this context. My API is working great and I don't want to rewrite my modelserializers to serializers just so I can get better automatic documentation through swagger.

ModelSerializers are the right way to go with DR-Swagger. It can be a bit tricky chasing down exactly where the different Swagger fields are extracted from though, I often had to fall back to step-debugging through the page rendering process in order to figure out where things were coming from.
In turn:
Required? comes from the Field.required parameter (either set on the model or the Serializer field).
Description comes from the Field.help_text parameter.
In the new-style DRF serialization, the description text comes from the ViewSet's docstring. If you want method-specific docs, you need to override the docstring for individual methods, e.g. retrieve:
def retrieve(self, request, *args, **kwargs):
"""Retrieve a FooBar"""
return super().retrieve(request, *args, **kwargs)
One thing to note is that DR-Swagger migrated to using the new DRF schema logic in version 2.0 (with DRF version 3.5), which has a few rough edges still. I recommend sticking with DR-Swagger version 0.3.x, which (though deprecated) has more features and in my experience, more reliable serialization.

In most cases ModelSerializer is what you need, because it can be greatly customized to suit your needs. In ideal situation you should define all your constraints, like required attribute on a field, in your model class, but there are times when it's not architecturally possible, then you can override such a field in your ModelSerializer subclass:
from django.contrib.auth import get_user_model
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
first_name = serializers.CharField(required=True)
last_name = serializers.CharField(required=True)
class Meta:
model = get_user_model()
In the example above I serialize standard User model from Django and override required attributes so, that first_name and last_name are now required.
Of course, there are cases when it's hard or impossible to use ModelSerializer then you can always fallback to Serializer subclassing

In the code you have:
"""
This text is the description for this API
param1 -- A first parameter
param2 -- A second parameter
"""
Try:
""" This text is the description for this API
param1 -- A first parameter
param2 -- A second parameter
"""
I have found some python and/or Django plugins need the docstring's first line, which is the one with the opening three double-quotes to also be the line that starts the documentation. You might even want to try no space between the last double-quote and the T.

Related

How to do typing in django model field?

With the recent addition of inlay types for python in VS Code I noticed that the typing for a Field will look like this:
As you can see it is Field[Unknown, Unknown]. And as you know if you don't provide a type to a field, you won't get attribute hints for the field, and the field will be shown as Unknown.
You could just provide an str type if you for example have a CharField, something like this:
field: str = models.CharField()
The problem is, if you want to use a strongly typed linter - it will show you an error, that the assigned value is not of type str.
So I saw this inlay and I started playing around with this generic, and I noticed that the second parameter of the generic will be the type used to represent the field attribute:
My question is, does anyone know what is the first parameter of the generic used for, and where is this generic even created, because inside of the package I see that the Field class does not inherit any Generics.
Django does not allow mutating fields since a change to a field of a model would lead to a database migration.
Nevertheless under the hood many fields use the same types and are basically replaceable. I.e. ImageField just stores a path to a string similar to what i CharField could do. Allthough the inner representation of data, or how the data is stored in the field might be different for some fields.
Still all of the fields come with a huge functionality and are usually deeply embedded and wired into the framework. Therefore django model fields are not generic. I guess your IDE is doing something, ... not appropriate :)
In the documentation you can find additional information on fields. Here you can find a list of all built-in fields.
edit:
I was thinking some more about the issue. almost all fields, I believe all of them, extend the models.Field class. So this might be the reason your IDE is doing this. Some polymorphic thingy has been activated in the code generator or something.
I think that the best way to type a field to get attribute hints (methods and attributes) of the value of the field and don't get linting errors when using strong or basic typing would be to use Union and do something like this:
username: str|CharField = CharField()

DJango filter_queryset

I am new to DJango and DRF and have been asked to manage some DJango/DRF related code. After a lot of search I am still unable to find a complete example on how filter_queryset works and how can it be used with different arguments.
At some places I see it used like the following,
self.filter_queryset(queryset)
and at other places it is used with some arguments. It would be helpful if some one can explain the fundamentals like how and when to use it, what are the dependent variables (lookup_field, filter_backends etc...) and arguments and how to set them up.
I have searched a lot and also gone through the docs. If i have missed any doc kindly let me know.
The filter_queryset()--(source code) is a method which is originally implemented in GenericAPIView -- (DRF doc) class.
def filter_queryset(self, queryset):
"""
Given a queryset, filter it with whichever filter backend is in use.
You are unlikely to want to override this method, although you may need
to call it either from a list view, or from a custom `get_object`
method if you want to apply the configured filtering backend to the
default queryset.
"""
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
I think the functionality of the method is clearly visible from the doc strings.
".....and at other places it is used with some arguments"
The views's filter_queryset() method takes only one parameter, which is the queryset to be filtered.
But, filter-backends' filter_queryset() method takes three arguments which are request,queryset and the view itself.
What are FilterBackends?
Filterbackends are classes which helps us to filter the queryset with complex lookups and some other stuff.
DRF has few built-in backends which can be found here.DRF official docs recommend to use django-filter package for advanced filtering purposes.
How filter-backend working?
Take a look at the source code of DjangoFilterBackend class and it's methods...It's filter_queryset(...) method plays key role in the filtering process.
I would recommend to go through the doc of django-filter to understand the usage of the same with more examples.
By defining filterset_class, you could've more controll over the filtering process (such as providing lookup_expr etc)

How to describe parameters in DRF Docs

I'm using Django REST Framework v3.6 built-in interactive documentation django_rest_framework.documentation (not django-rest-swagger).
Basically, I'm following the official documentation and use this in my URLset configuration:
from rest_framework.documentation import include_docs_urls
urlpatterns = [
url(r"^", include_docs_urls(title="My API")),
...
]
Everything seems to work and I get a nice interactive documentation page, but I have a ViewSet with lookup_field = "slug" and one thing about the generated documentation bothers me:
I want to have some useful information it that description, like "an unique permanently-assigned alphanumeric ID" or something among those lines, but can't find any documentation where this data comes from.
There is a workaround but I really don't want to define all the schema explicitly. I want to declare my classes with nice docstrings and have docs auto-generated. I've also found an suggestion to put slug -- here goes the description in the docstring but it doesn't seem work - the text just appears with the rest of the Markdown-formatted description.
So... I wonder about two things:
(A specific question) Where do I fill this path parameter description?
(More generic version of the same question) What's the best way to learn how schemas are auto-generated from code?
Oh, I found it. Answering my own question.
DRF documentation isn't verbose on this matter (or I've missed the piece where it is), but it mentions rest_framework.schemas.SchemaGenerator class and it seems that this class really does all the introspection stuff. Fortunately, the source code is well-structured and easy to read.
Those path fields are generated by get_path_fields method (I found it by tracing the execution path: get_schema → get_links → get_link), and I found that descriptions come from model fields's help_text attribute.
So in my model I've specified:
class MyResource(models.Model):
slug = models.CharField(unique=True, help_text=_("unique alphanumeric identifier"))
...
And it worked!
One important thing was not still covered. It is true that a description comes from the help_text attribute, but this is not enough. I have found that the schema generator rely on view's queryset attribute to determine a model. So, keep in mind that you need define it even if you don't need it. For example in case of using APIView.

Django Rest Frame API: additional fields in ModelSerializer

Can I add additional fields to ModelSerializer subclass?
By saying 'additional field', I mean some fields don't belong any models in database, which any effort that try to map those fields to exist model fields will fail. The reason I need to include those fields is for design purpose. I need to those fields' value to do the validation and creating a new instance eventually.
I know there is a kwarg in ModelSerialzer called 'context'. By putting all the additional information into 'context', it will work. However, I want to know is that possible to create additional fields?
I have tried adding 'write_only=True', which doesn't work. The only left option is to override default restore_object method to create the instance with my will.
Any other ideas?
As you have not posted any code I can only give you a generic answer, but if I understand you correctly, you wish to add a custom field to a ModelSerializer thats not part of your model...
In DSF you can do this very esaily (read here):
In this case you just want a simple read-only field, so instead just use:
custom_field = Field(source='get_whatever')
In terms if validation after that please read the DRF guide here

More than one profile in Django?

Is it possible to use Django's user authentication features with more than one profile?
Currently I have a settings.py file that has this in it:
AUTH_PROFILE_MODULE = 'auth.UserProfileA'
and a models.py file that has this in it:
from django.db import models
from django.contrib.auth.models import User
class UserProfileA(models.Model):
company = models.CharField(max_length=30)
user = models.ForeignKey(User, unique=True)
that way, if a user logs in, I can easily get the profile because the User has a get_profile() method. However, I would like to add UserProfileB. From looking around a bit, it seems that the starting point is to create a superclass to use as the AUTH_PROFILE_MODULE and have both UserProfileA and UserProfileB inherit from that superclass. The problem is, I don't think the get_profile() method returns the correct profile. It would return an instance of the superclass. I come from a java background (polymorphism) so I'm not sure exactly what I should be doing.
Thanks!
Edit:
Well I found a way to do it via something called an "inheritance hack" that I found at this site http://djangosnippets.org/snippets/1031/
It works really well, however, coming from a java background where this stuff happens automatically, I'm a little unsettled by the fact that someone had to code this up and call it a "hack" to do it in python. Is there a reason why python doesn't enable this?
So the issue you're going to have is that whatever you want for your profile, you need to persist it in a database of some sort. Basically all of the back-ends for django are relational, and thus every field in a persisted object is present in every row of the table. there are a few ways for getting what you want.
Django provides some support for inheritance. You can use the techniques listed and get reasonable results in a polymorphic way.
The most direct approach is to use multiple table inheritance. Roughly:
class UserProfile(models.Model):
# set settings.AUTH_PROFILE_MODULE to this class!
pass
class UserProfileA(UserProfile):
pass
class UserProfileB(UserProfile):
pass
To use it:
try:
profile = user.get_profile().userprofilea
# user profile is UserProfileA
except UserProfileA.DoesNotExist:
# user profile wasn't UserProfileB
pass
try:
profile = user.get_profile().userprofileb
# user profile is UserProfileB
except UserProfileB.DoesNotExist:
# user profile wasn't either a or b...
Edit: Re, your comment.
The relational model implies a number of things that seem to disagree with object oriented philosophy. For a relation to be useful, it requires that every element in the relation to have the same dimensions, so that relational queries are valid for the whole relation. Since this is known a-priori, before encountering an instance of a class stored in the relation, then the row cannot be a subclass. django's orm overcomes this impedance mismatch by storing the subclass information in a different relation (one specific to the subclass), There are other solutions, but they all obey this basic nature of the relational model.
If it helps you come to terms with this, I'd suggest looking at how persistence on a RDBMs works for applications in the absence of an ORM. In particular, relational databases are more about collections and summaries of many rows, rather than applying behaviors to data once fetched from the database.
The specific example of using the profile feature of django.contrib.auth is a rather uninteresting one, especially if the only way that model is ever used is to fetch the profile data associated with a particular django.contrib.auth.models.User instance. If there are no other queries, you don't need a django.models.Model subclass at all. You can pickle a regular python class and store it in a blob field of an otherwise featureless model.
On the other hand, if you want to do more interesting things with profiles, like search for users that live in a particular city, then it will be important for all profiles to have an index for their city property. That's got nothing to do with OOP, and everything to do with relational.
The idios app by the Pinax team aimed at solving the multiple-profile problem. You can tweak the model to make the inheritance of the base profile class either abstract or non-abstract.
https://github.com/eldarion/idios.
Here is the answer to my question of how to get multiple profiles to work:
from django.contrib.contenttypes.models import ContentType
class Contact(models.Model):
content_type = models.ForeignKey(ContentType,editable=False,null=True)
def save(self):
if(not self.content_type):
self.content_type = ContentType.objects.get_for_model(self.__class__)
self.save_base()
def as_leaf_class(self):
content_type = self.content_type
model = content_type.model_class()
if(model == Contact):
return self
return model.objects.get(id=self.id)
I don't really understand why it works or why the developers of django/python made inheritance work this way
If you have app-specific options for each user, I would rather recommend to put them into a separate model.
A simplified example:
class UserSettings(models.Model):
user = models.ForeignKey(User, primary_key = True)
# Settings go here
defaultLocale = models.CharField(max_length = 80, default = "en_US")
...
This would be used like so:
def getUserSettings(request):
try:
return UserSettings.objects.get(pk = request.user)
except UserSettings.DoesNotExist:
# Use defaults instead, that's why you should define reasonable defaults
# in the UserSettings model
return UserSettings()

Categories

Resources