Django Rest Framework call custom function when model is created - django

Suppose I want to do some extra stuff whenever a model is created in my Django App:
models.py:
class MyModel(models.Model):
name = models.CharField(max_length=255)
def do_some_extra_stuff(self):
// whatever...
return "I did some extra stuff"
Suppose the extra stuff is some long-running asynchronous task.
I am using Django Rest Framework and have a standard ModelSerializer and ViewSet.
My question is where is the best place to put the call to do_some_extra_stuff ? I can think of the following options:
In the serializer.create method
In the viewset.create method
In the model.save method
In a post_save signal
I am leaning towards #1 or #2 b/c I only want this fn to be called when normal users interact w/ the API - not when using the Django Admin or the shell.

This question will probably be closed because its opinion based, so here are my opinions on it!
Background Tasks
Use Celery or Django-RQ.
Celery is "better", but Django-RQ (and RQ itself) is almost always enough if you have good logging and error reporting. These aren't usually bank account transfers we're dealing with.
#2 - Viewset Based
I believe serializers should only serialize & validate. Because of this, any custom save logic needs to go in the ViewSet - up front, visible at the point use, and has access to everything without any serializer context tricks.
class MyViewSet(ModelViewSet):
def perform_create(self, serializer):
# pass a value into save(), which passes it to model.create()
serializer.save(org_id=self.request.user.secret_org_id)
# use the serializer instance that was created to enqueue a bg task
my_background_task.delay(item_pk=serializer.instance.pk)
This is just a preference. Its perfectly fine to put it in the serializer if that is how you normally do other tasks. The only goal is consistency.
This may make more sense if you imagine using 1 serializer for each action - list, create, and update.
Other Methods
#3 In the model.save method
The model.save() is not called in some rare cases, and its not the place I expect to see logic that runs other tasks. Some model field cleanups work there. Again this is a preference, but using this method with DRF seems unusual, as opposed to when you are in pure django and using the admin site.
#4 In a post_save signal
I feel post-save signals are too far away from the model, and also not called in some rare cases (same mostly as #3). I would rather be explicit with things like this, so I keep it close to where I expect to see it happen. We use post_save signals for other things, but they do suffer from not being able to see the context, and its difficult to pass additional parameters into them.

Related

Django rest framework where to write complex logic in serializer.py or views.py?

I am new to Django Rest Framework. Using serializer and views a simple CRUD is easy. When the logics increase, it is quite confusing where to write logics in serializer or views.
Some developers do prefer "Thick serializer and thin views" and some developers "Thick views and thin serializer".
Maybe this is not a big issue and I think it is up to the developer whether to write more on views or serializer, but as a newbie what will be your suggestion to follow? Should I write more on views or serializer?
There are many answers on Django View Template but can not find a satisfying answer for Django Rest Framework.
Thoughts of experienced developers will be highly appreciated. Thank you.
Personally I prefer to have the business logic separated from both view and serializer. I usually create a new class with the business logic which can be used in both serializer and view based on necessity. Basically I treat it as a service. Reason for that is:
Makes the code cleaner(no thick and thin stuff).
Writing tests for those business logic is easier.
You can use that this business logic service in both view and serializer, depending on your need.
while testing APIs, you can mock those buiness logic if needed.
Reuse any logic in multiple places.
An example would be like this:
class BusinessLogicService(object):
def __init__(self, request):
self.request = request
def do_some_logical_ops(self, data_required_one, data_required_two):
# do processing
return processed_data
Example usage in serializer:
class SomeSerializer(serializer.Serialize):
...
def create(self, validated_data):
business_logic_data = BusinessLogicService(self.request).do_some_logical_ops(**validated_data)
return Model.objects.create(**business_logic_data)
I've asked a similar question about this before. Actually, it depends on what logic you are going to adapt. After doing further research, I have come up with some approaches. Here are my suggestions:
If you want to add some logic before doing serializer validation, it is better to include that in your view (eg. override your views create method). An example to this case would be; your POST body does not contain a value that is needed by the serializer hence is not valid yet, so add that in your view's create function.
If you are doing some custom authentication logic, such as parsing a custom token in your http header, do it in your view as well, because serializer has nothing to do with it. Moreover, you can create your own authentication decorator for that.
If you want to add logic which is directly related to the representation of your data, such as an adaptation of a timestamp from UTC to some other, you can add in your serializer as it is directly related to your object representation. You can use SerializerMethodField etc. to do that.
I try to segregate it on the basis of requirements of the context. Like:
If I'm supposed to deal with the request object(like current user), I try to implement that in view.
If I need to deal with querying models to create a view I would switch to serializer.
It may also depend upon how I'm planning to maintain the code (If I have huge number of views in a single file, I will try to avoid implementing logical stuffs as much as possible).

Good Django design practice to add a REST api later following DRY

I am starting a web application in pure Django. However, in the future, there might be a requirement for REST api. If it happens, the most obvious choice will be Django REST framework.
Both the "old-fashioned" and REST parts share the models, however, the views are slightly different (permissions definitions, for example) and forms are replaced with serializers. Doing it the most obvious way would mean to duplicate the application logic several times, and thus a failure to follow DRY principle, and so the code becomes unmaintainable.
I got an idea to write all the logic into models (since they are shared), but in such case, there will be no use of permission mixins, generic views and the code would not be among the nicest ones.
Now I ran out of ideas. What is the best practice here?
I'd try to keep things simple as you're not sure about the future requirements for the API, and guessing can introduce extra complexity that may not even be needed when requirements will be clear.
Both Django forms and Rest Framework serializers already offer you a declarative approach that abstracts away the boilerplate code needed for basic stuff, which normally accounts for most of your code anyway.
For example, one of your Django form could look like this:
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['title', 'content']
And in the future the DRS serializer would be:
class ArticleSerializer(ModelSerializer):
class Meta:
model = Article
fields = ['title', 'content']
As you can see, if you try and stick to ModelForm and ModelSerializer, there won't be much duplication anyway. You can also simply store the fields list in a variable and just reuse that.
For more custom things, you can start by sharing logic into simple functions, for example:
def save_article_with_author(article_data, author_data):
# custom data manipulation before saving, consider that article_data will be a dictionary either if it comes from deserialized JSON (api) or POST data
# send email, whatever
This function can be shared between your form and serializer.
For everything related to data fetching, I'd try to use Model Managers as much as possible, defining custom querysets that can be resued e.g. for options by forms and serializers.
I tend to avoid writing any logic that doesn't directly read or write data into the model classes. I think that couples too much the business logic with the data layer. As an example, I never want to write any auth/permission checks into a save() method of a model, because that couples different layers too tightly.
As a rule of thumb, imagine this scenario: you add say permissions checks or the logic to send an email when a user is created overriding the save() method of your Article model.
Then, later on you're asked to write a simple manage command that batch-import users from a spreadsheet. At this point, what you did in your save() method really gets in the way, as you can freely access your data through your model without having to bother with permissions, emails and all of that.
Regarding the view layer and assuming you need to implement some shared auth/permission checks and you don't want to have separate views, you can use this approach:
https://www.django-rest-framework.org/topics/html-and-forms/
Blockquote
REST framework is suitable for returning both API style responses, and regular HTML pages. Additionally, serializers can be used as HTML forms and rendered in templates.
Here's some guidelines on how you could dynamically switch from HTML to JSON based to the request content type:
https://www.django-rest-framework.org/api-guide/renderers/#advanced-renderer-usage
This seems like a good option in your situation, I'd just write down a quick proof-of-concept before you go all in to see if you are not too limited for what you need to do.

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.

django form methods are for validation only or not?

While I am studying the new django docs on class-based views, I notice this example code:
# forms.py
from django import forms
class ContactForm(forms.Form):
name = forms.CharField()
message = forms.CharField(widget=forms.Textarea)
def send_email(self):
# send email using the self.cleaned_data dictionary
pass
The fact of seeing send_email as a method of ContactForm really annoys me. I have always thought form methods should be for validation purpose and methods that consume forms (like send_email in this case) should be in the views layer. Am I missing anything here? Or should the example be corrected?
There is no correct answer for this one. It really depends on your coding style. Using a form for more than just validation is as valid as doing this method in the view.
The above example has some kind of advantage though. Lets say you have one model and you want to use different forms (with different logic) for it. Instead of putting the logic in the view and check which form is now used it probably better to put this logic on the form level.
The first time I got the same weird feeling was when I encountered the LoginForm from django.contrib.auth that also verifies that the user's browser is capable of working with cookies aside from managing the credentials passed in.
Personally, I agree with you. I'm more inclined to think that the view should be the responsible actor for performing the action of sending the email rather than the form and that send_email should be a method defined in the view.
But, then again, you can easily observe how a lot of us use Django differently or have a different approach to solving the same problems. Due to the fact that we have different concerns in mind when developing our applications, it's quite possible we have a different understanding of how certain framework components are meant to be used, which is a bit of a moot point. In the end, what's important to acknowledge is that it's possible to delegate some of the heavy-lifting from the view to the form in a manner that's well-defined.
There is nothing wrong with the example. For any but the simplest of cases, your form will contain only cleaning methods; most forms do extra validation and other actions. In the end, it is just a Python class and you can do whatever you would like in it; there is no "rule" except maybe DRY.
We have form methods that validate against external services, call other procedures and trigger workflows. For such complicated logic, it is best to embed it in to the form class as it increases reusability of the code. Developers working on the view simply use the form class as a library and don't have to worry about exotic validation requirements. In this case, it actually promotes DRY.
When we have to update the logic we only update the form class's "private" methods (those prefixed with _). This keeps the "public" interface (documented by django) intact and all view code using that form doesn't have to be updated.

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.