In Django DRF, I can add the following method to a serializer:
def create(self, validated_data):
user = User(
email=validated_data['email'],
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
Previous to discovering this, I thought you typically did this within a view, by overriding a View/ViewSet methods.
I had thought that all the Serializer did was to transform the data and send it out or receive it from external calls.
Can someone explain to me the difference between doing this on the view vs doing it on a serializer?
And more broadly, what is the benefit of calling methods on the serializer instead of somewhere else?
A serialiser's job is to take one blob of data and convert it into another blob of data. Most typically it converts from a model to some form of dict and vice versa. You typically have at least one serialiser per model, but you may have any number of serialisers for different use cases. For example, you may get data in different forms, e.g. from a registration form and from an API call, and you want to convert both into a valid User instance. So you may define two different serialisers for those two different scenarios, which both end up with the same User instance. And you may need to customise some aspects of those serialisers to fit the occasion.
Views on the other hand take an HTTP request, do something and then decide what response to return. That doing something may involve using serialisers, but it doesn't have to. The biggest job of a view is to decide what to do when something succeeds or fails, like rendering a different response or redirecting to a different URL.
You need to decide how reusable some piece of logic is. Think of serialisers as transforming one type of data into another; call it converting type A into type B and vice versa. "Type" here being what your input/output data looks like exactly. Then consider where in your application you'll encounter a data blob of type A and whether you'll need to convert it into type B more than once. If so, you'll probably want to make a specific serialiser for it, instead of repeating the same logic inside two or more views.
Related
Suppose I want to make an API that is not supported by a model. Certain fields are entered with the request, some processing is applied, and then other fields are returned.
The way I usually use it is to define a simple view that instantiates a serializer to do the validation. And then I return the fields directly from the view, for example with a JSONResponse.
Is this the right way to do it? Does it make sense to use a serializer for validation? Is it better to use a form? Should the response be serialized? Is it correct to process the input data in the view?
I would like to redact object instances based on the user's view permission.
For instance, if I have the following model
class Data(models.Model):
value = models.FloatField()
I would like users that have view permission, i.e. myapp.view_data, to see the full value, and users without view permission to only see a redacted version, say round(value).
I could implement this for every view and template, but that doesn't seem very DRY and would have a risk of overlooking some occurrences. Also, I suppose in a template the mechanism might be bypassed through relation queries.
How can I implement this redaction mechanism in the model? Would this be good practice? Or is there a better way?
Thank you!
You can make a class method called get_modified_value(user). This would basically be a wrapper for the value field. It would take a User object as a parameter. Based on that user's permissions, it would either return the raw value or the rounded value.
I recommend this because Models are completely unaware of context or the http request. So no matter what, every time you want that value, you are going to need to do some sort of manipulation in the view. It seems cleanest to me to pass user to the model method but that might have its own problems.
I've a serializer that receives and returns a list of strings.
Those strings internally are model instances.
In the serializer, when I receive a new types list I've to check if the type exists, if exists associate it to the other model otherwise create a new instance and associate it.
I can do it using a custom RelatedField
class TypeRelatedField(serializers.StringRelatedField):
def to_internal_value(self, data):
try:
return Type.objects.get(name=data)
except Type.DoesNotExist:
return Type.objects.create(name=data)
and in the serializer that receives the types list
types = TypeRelatedField(many=True, required=False)
so that if the type exists it will be returned, otherwise created. I'm not sure if it's the right place to do this, maybe I should do this in the create and update method?
If it works and doesn't break anything else, then it's a right thing to do ;) If you have to do this an all methods that manipulates model (create, update), then it's probably best to do it here, for DRY reasons. If not, do it in create or update. But if you need it for example only on create, you should write it in create, if only in update, then it should go there.
Being a newb I'm trying to work out what belongs where. I've got a ListView to list all users in the system, and it includes a get_queryset method -something along the lines of:
def get_queryset(self):
users = []
for user in User.objects.all():
a_user = {}
a_user['username'] = user.username
a_user['full_name'] = user.get_full_name()
a_user['num_of_friends'] = len(user.friends.all())
a_user['phone_num'] = user.get_profile().phone_num
a_user['has_subscription'] = bool(Subscription.objects.filter(subscriber=self.request.user))
users.append(a_user)
return users
So rather than returning a queryset of users I'm making this dictionary out of various chosen attributes of each user, and what a template designer gets is limited to only what I think they should have.
Is it better practice to instead pass user objects to the template, and let the template writer get whatever they need from them? I suppose the answer is Yes.
But, when it comes to the a_user['has_subscription'] line above, I assume the answer becomes No?
The way you've done it is totally fine. I've found it to be useful to separate my templates from my model by explicitly providing the information needed by the template in the view, which is essentially what you've done. It's a little bit weird to not be returning an actual queryset from a method called get_queryset, but I've also come to the conclusion based on criticisms like this that class-based views as they are currently implemented in Django should be considered a means to an end, as opposed to a dogmatic way of organizing your view code. The abstraction simply isn't as clean as say, the Django ORM.
If you really wanted something a little slicker, you could try using the values method to narrow your queryset, with an annotation to add in the subscription count. But there's probably not much point.
It depends on what you're trying to do with the array 'users'. Someone editing the template can only style and display the data in a certain way. The get_queryset function can provide the data for the template.
I am working on an app which would enable a preview function for a model. Models marked as preview-able would allow for changes to be made in the Django admin interface and previewed on site using the same view as would an object of that type normally use to render itself, but rendered instead with the new (unsaved) object in it's place.
This is a pretty easy task to do in a bespoke fashion when you know the views or templates ahead of time. But I want this to be reusable and simple.
What I Was Thinking
My idea would be to apply the resolve() urls function to the existing (saved) object's get_absolute_url() value to discover the view used dynamically. Then call that view, get the returned HTTPResponse and alter it in some fashion before returning it myself.
The Problem
It seems that by the time the HTTPResponse is returned by the object's natural view the HTML has already been rendered by the template engine.
So I guess the question is: Is there a way to get at a HTTPResponse before the template is rendered and alter the context variables before that happens.
If not then could the same idea be implemented in another fashion. Would using middleware change anything (your still working with a HTTPResponse object there as well).
Thanks,
Marcus
P.S. If you come up with a radically different methodology to solve this problem, I will be sure to attribute that concept to you in the app documentation (despite it being a small app right now).
It is not trivially possible no, the easiest way would actually be to write your own template context processor that checks for example if something like GET['preview'] is set, then sets dictionary values based on some other GET or POST data. Furthermore when other variables are added it should make sure these don't overwrite the existing values set by this method (otherwise the view would override it anyway with some other data).
One remark however: completely unintrusive behaviour will often lead to erroneous behaviour. If the view does not know of this preview functionality and e.g. it expects a valid id or redirects to an error page, your preview won't work (as you don't really have a valid id). Choosing for views that know of this preview functionality is indeed some more work but will certainly be correct. You could try to make it more generic by using decorators or callable view classes (which can be derivable from some generic base) instead of view methods.
A completely different approach that is only slightly 'view' intrusive, I assume you do not want to save the model so it doesn't show up in public listings. An approach could be to add a 'preview' field and use managers to restrict lookups, so something like this:
class NoPreviewManager(models.Manager):
def get_query_set(self):
return super(MyModelManager, self).get_query_set().filter(preview=False)
class MyModel(models.Model):
... #other fields
preview = models.BooleanField()
objects = NoPreviewManager()
allobjects = models.Manager()
In all normal views you can just use MyModel.objects so previews won't be listed. In the object-specific view you use MyModel.allobjects to also enable defailed views of previews. This way you don't have to do weird view hijacking things, but you should take care preview objects get cleaned up if they aren't promoted to real objects. Note that you can also combine many of this logic into a base class.