I don't understand how Django-Rest-Framework serializers work - django

This may seem like a dumb question but I really feel like Django-Rest-Framework does not explain really well how it works. There is too much black box magic which confuses the configuration instructions (in my opinion).
For instance, it says I can override create or update within my serializer. So when my view post, I can send data to the serializer which has an update method declared.
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
def update(self, instance, validated_data):
Does update only get called when the model already exists and we're just updating some of it's data? Or does it call create when it creates a new one?
if I were to add this method to that class,
def create(self, validated_data):
return MyObject.objects.create(**validated_data)
is this specifically the method that must be called in order to add a new object? and your ability to override should be put in the serializer, but if not declared this is the default method with parameters that's being called?

There is too much black box magic which confuses the configuration instructions (in my opinion).
If there is something in the documentation that you think can be improved, feel free to submit a pull request with an update. If it seems reasonable, it will probably be merged and show up in a future release.
Or does it call create when it creates a new one?
create is called when a serializer is not initialized with a model instance, but only data is passed into it. Once serializer.save() is called, the serializer check if an instance was passed in and directly calls create so the model instance is created and saved in the database.
Does update only get called when the model already exists and we're just updating some of it's data?
update is called when a serializer is initialized with a model instance (or other object) and serializer.save() is called. This is roughly equivalent to Model.objects.update() when compared to Model.objects.create().
is this specifically the method that must be called in order to add a new object?
Serializers are designed to be saved (including object creation and updating) using the central serializer.save() method. This is similar to how a model can be saved or created using the model.save() method.
and your ability to override should be put in the serializer, but if not declared this is the default method with parameters that's being called?
It is recommended to override create and update on the serializer level if you need to change the logic for how a model and its related objects need to be saved, such as when working with nested serializers.

Related

Custom Related field

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.

Django Model vs. Manager

Not really sure what the difference is. Seems like all the Manager does is have a bunch of functions related to the Model. But these functions could also be placed in the Model too....
Django documentation describes the Manager as follows,
A Manager is the interface through which database query operations are
provided to Django models.
So is there anything else fundamentally different about the Manager than this simple abstraction?
Or a better question: what methods should be defined in the Model vs. the Manager? Is there an ACTUAL difference or just stylistic one?
In Django, a models' manager is the object through which models perform database queries. Each Django model has at least one manager, which is objects, and you can create your own to change the default behavior.
So, your statement
But these functions could also be placed in the Model too
Well, not really because the model is still depending on the default manager to retrieve the queryset.
Let me try to explain in terms of an example. Lets say your application requires a model object to show only objects with a status of published. Now, MyModel.objects.all() retrieves everything, and you would have to specify the filter MyModel.objects.filter(published=True) every single time.
Now, you can override this default behavior.
class MyModelAdmin(admin.ModelAdmin):
def queryset(self, request):
return MyModel.objects.filter(published=True)
What we just did was override the default behaviour of the default manager.
Now, lets say you want everything, You can do something like
class MyModelAdmin(admin.ModelAdmin):
def queryset(self, request):
return MyModel.objects.filter(published=True)
def all_objects(self, request):
return MyModel.objects.all()
and while accessing all objects, just do
MyModel.objects.all_objects()
It is also possible to have multiple managers to a single model
In short, managers give a lot of flexibility in terms of accessing querysets to the model.

Django - How to trigger an action when a specific model is saved?

I have two models. One called MainModel and other called HistoricMainModel. I would like to INSERT automatically a row in the HistoricMainModel every time I data is inserted to MainModel. What is best/correct way of doing this in Django?
Best Regards,
If you already have some custom save().-magic going on I would recommend using a post_save() signal or a pre_save() which ever would work best for you.
in your models.py
#receiver(pre_save, sender=MainModel)
def save_a_historicmodel(sender, **kwargs):
#do your save historicmodel logic here
or
def save_a_historicmodel(sender, instance, created, **kwargs):
print "Post save was triggered! Instance:", instance
signals.post_save.connect(save_a_historicmodel, sender=MainModel)
This works so that every time your MainModel is saved this signal is triggered.
Docs here
Rewrite save method in MainModel model to create and insert the HistoricMainModel object before calling the "real" save method.
You could implement this yourself but the best way would be to use an application that does this - like reversion - a django app that implements model history/roll back automatically.
In addition to whatever you will implement, reversion will provide you with:
Integration with the admin backend.
A API to add meta-data to your versions and to revert back to previous versions of your model.
It is a lot more flexible and is widely used and implemented.

confused about self.instance in save() of child of ModelForm()

The save() documentation explains that:
A subclass of ModelForm can accept an
existing model instance as the keyword
argument instance; if this is
supplied, save() will update that
instance. If it's not supplied, save()
will create a new instance of the
specified model
However, self.instance in save() always has an object.
So, how do I tell if the instance is existing or a newly created one?
You can check self.instance.pk to see if the model has previously been saved. However, that could be unreliable in the case where you created a new instance of the model and then initialized a modelform with that instance before saving it.
Another possibility, based on the BaseModelForm source code in Django 1.2, is to check self.instance._adding, which will be True if the model was created and False otherwise. However, I haven't tested this, so YMMV.
If the first option will work, I'd recommend using that rather than an undocumented feature of ModelForms--it's less likely to change in the future and probably clearer.

What are the options for overriding Django's cascading delete behaviour?

Django models generally handle the ON DELETE CASCADE behaviour quite adequately (in a way that works on databases that don't support it natively.)
However, I'm struggling to discover what is the best way to override this behaviour where it is not appropriate, in the following scenarios for example:
ON DELETE RESTRICT (i.e. prevent deleting an object if it has child records)
ON DELETE SET NULL (i.e. don't delete a child record, but set it's parent key to NULL instead to break the relationship)
Update other related data when a record is deleted (e.g. deleting an uploaded image file)
The following are the potential ways to achieve these that I am aware of:
Override the model's delete() method. While this sort of works, it is sidestepped when the records are deleted via a QuerySet. Also, every model's delete() must be overridden to make sure Django's code is never called and super() can't be called as it may use a QuerySet to delete child objects.
Use signals. This seems to be ideal as they are called when directly deleting the model or deleting via a QuerySet. However, there is no possibility to prevent a child object from being deleted so it is not usable to implement ON CASCADE RESTRICT or SET NULL.
Use a database engine that handles this properly (what does Django do in this case?)
Wait until Django supports it (and live with bugs until then...)
It seems like the first option is the only viable one, but it's ugly, throws the baby out with the bath water, and risks missing something when a new model/relation is added.
Am I missing something? Any recommendations?
Just a note for those who run into this issue as well, there is now an built-in solution in Django 1.3.
See the details in the documentation django.db.models.ForeignKey.on_delete Thanks for editor of Fragments of Code site to point it out.
The simplest possible scenario just add in your model FK field definition:
on_delete=models.SET_NULL
Django only emulates CASCADE behaviour.
According to discussion in Django Users Group the most adequate solutions are:
To repeat ON DELETE SET NULL scenario - manually do obj.rel_set.clear() (for every related model) before obj.delete().
To repeat ON DELETE RESTRICT scenario - manually check is obj.rel_set empty before obj.delete().
Ok, the following is the solution I've settled on, though it's far from satisfying.
I've added an abstract base class for all my models:
class MyModel(models.Model):
class Meta:
abstract = True
def pre_delete_handler(self):
pass
A signal handler catches any pre_delete events for subclasses of this model:
def pre_delete_handler(sender, instance, **kwargs):
if isinstance(instance, MyModel):
instance.pre_delete_handler()
models.signals.pre_delete.connect(pre_delete_handler)
In each of my models, I simulate any "ON DELETE RESTRICT" relations by throwing an exception from the pre_delete_handler method if a child record exists.
class RelatedRecordsExist(Exception): pass
class SomeModel(MyModel):
...
def pre_delete_handler(self):
if children.count():
raise RelatedRecordsExist("SomeModel has child records!")
This aborts the delete before any data is modified.
Unfortunately, it is not possible to update any data in the pre_delete signal (e.g. to emulate ON DELETE SET NULL) as the list of objects to delete has already been generated by Django before the signals are sent. Django does this to avoid getting stuck on circular references and to prevent signaling an object multiple times unnecessarily.
Ensuring a delete can be performed is now the responsibility of the calling code. To assist with this, each model has a prepare_delete() method that takes care of setting keys to NULL via self.related_set.clear() or similar:
class MyModel(models.Model):
...
def prepare_delete(self):
pass
To avoid having to change too much code in my views.py and models.py, the delete() method is overridden on MyModel to call prepare_delete():
class MyModel(models.Model):
...
def delete(self):
self.prepare_delete()
super(MyModel, self).delete()
This means that any deletes explicitly called via obj.delete() will work as expected, but if a delete has cascaded from a related object or is done via a queryset.delete() and the calling code hasn't ensured that all links are broken where necessary, then the pre_delete_handler will throw an exception.
And lastly, I've added a similar post_delete_handler method to the models that gets called on the post_delete signal and lets the model clear up any other data (for example deleting files for ImageFields.)
class MyModel(models.Model):
...
def post_delete_handler(self):
pass
def post_delete_handler(sender, instance, **kwargs):
if isinstance(instance, MyModel):
instance.post_delete_handler()
models.signals.post_delete.connect(post_delete_handler)
I hope that helps someone and that the code can be re-threaded back into something more useable without too much trouble.
Any suggestions on how to improve this are more than welcome.