Hook into django field assignment - python

When creating a "vanilla" python class, if one wants to execute code on assignment to an attribute, the usual pattern to to create a "protected" (leading-underscore) attribute to hold the data, and use property to create getters and setters.
Now, if I wanted to do that with a Django model, that would be unattractive, not least because I would have to search on the underscore-version of the property.
What is the recommended way to run code on field-assignment in Django?
There is no signal that I can find to do this; the only other approach I can see would be to use a custom Field that can register additional getter/setter handlers.

You simply can't do that on a non-custom field, because there is no actual "field": it's a simple attribute, with no class behind it.
You'll need to define a custom field class and override to_python - don't forget to set the metaclass to SubfieldBase.

It looks like dirtyfields is what you want. You can't intercept at exactly the same time the value is set, but you can hook in before saving. http://code.activestate.com/pypm/django-dirtyfields/
Alternatively, you should set up #properties with set methods. Of course this would require you to duplicate your actual django.db.models.fields fields with getters and setters.

Related

Why do Django REST Framework Renderer and Parsers have to be instantiated and do not offer their key methods as static/class methods?

Like Djangos rest_framework.JSONRenderer it needs to be instantiated like
json = JSONRenderer().render(serializer.data)
in order to use the render method.
What's the background, that render() is not offered as a static/class method?
Using static/class methods makes it impossible to override/hard to debug to use object specific tools such as adding instance variables if someone requires it.

Where is the declaration of objects of django model?

In django documentation, it say that we can retrieve data entry as below
entry = Entry.objects.get(pk=1)
Entry is a model class in models.py. I tried to find the declaration of objects, but I can't find its declaration in manager.py, just know it is a instance of Manager. So, where is the declaration of objects? Does it represent a set of Entry instances?
When you define model, you extend Model class from django.db.models module.
It will provide default model manager in objects property.
If you want to define custom model manager, you can do it by subclassing django.db.models.Manager class.
Look at the docs how to do that: https://docs.djangoproject.com/en/1.11/topics/db/managers/
Add methods to custom model managers to if you want to manipulate with the collection of data. If you manipulate with single row, add method to your model.
It's defined in ModelBase, which is the metaclass for model classes. See https://github.com/django/django/blob/master/django/db/models/base.py#L360

When are create and update called in djangorestframework serializer?

I'm currently implementing djangorestframework for my app RESTful API. After playing around with it, I still do not clearly understand what .create(self, validated_data) and .update(self, validated_data) used for in the serializer. As I understand, CRUD only calls the 4 main methods in viewsets.ModelViewSet: create(), retrive(), update(), and destroy().
I also have already tried to debug and print out stuff to see when the .create() and .update() methods are called in both ModelViewSet and ModelSerializer. Apparently, only the methods in ModelViewSet are called when I do the HTTP verbs. However, for ModelSerializer, I don't see any calls in those 2 methods. I just want to know what are those methods used for in ModelSerializer since I see that people override those methods a lot in the serializer.
You really must split things between the views and the serializer.
Serializers
The Serializer is a standalone object. It is used for converting a Django model (or any kind of python datastructure, actually) into a serialized form, and the other way around.
You may use it as such, wherever you want. It does not even need an actual HTTP request as long as you don't need URIs in your output.
The ModelSerializer subclass is a specialized kind of Serializer that adds "load-from-model" and "save-to-model" functionality.
The "save-to-model" entry point is the save() method. For easier overriding, its default implementation will delegate its work to either the create() or update() method of the serializer, depending on whether it is creating a new model instance, or updating one.
The purpose of that is customization: it gives you, the developer, the option to override just the create method, just the update method, or common behavior.
For instance, it allows you to do this kind of things:
def save(self, **kwargs):
# Will be done on every save
kwargs['last_changed'] = timezone.now()
return super().save(**kwargs)
def create(self, instance, data):
# Will only be done if a new object is being created
data['initial_creation'] = timezone.now()
return super().create(instance, data)
That's a basic example. There, the last_changed field will be set every time an object is saved, be it a creation or an update.
As a sidenote, you probably do not want to do that. Things such as setting "last_changed" fields should live in the view, not in the serializer.
Viewsets
In a completely different place, Django REST framework supplies Viewsets. Those are an organized collection of views, revolving around implementing a CRUD API for a model.
As such, it structures it functionality into a set of methods, namely create(), retrieve()/list(), update() and delete().
The main point being: there is no connection whatsoever between the viewset's create() method and the serializer's create() method.
It just happens that the default implementation of the viewset's methods uses a ModelSerializer and that the default implementation of that serializer's save() method delegates the job to methods that have the same name.
By the way, about the last_changed example, here is how you would do it in the view:
def perform_create(self, serializer):
now = timezone.now()
serializer.save(initial_creation=now, last_changed=now)
def perform_update(self, serializer):
serializer.save(last_changed=timezone.now())
That's functionally equivalent to the example above, but lives in the viewset.
Conclusion
So back to your question, the specific thing you should override depends on which object is responsible for the task you want to add.
If your custom behavior is part of the serialization process, that is, the process of converting raw data back into a proper Django model and saving it, then you should override the Serializer's methods.
If, on the other hand, your custom behavior is specific to your viewset, then you should override the Viewset's methods.
As a hint, you may ask yourself the following question: if I use the same serializer in another place (maybe another viewset), should it always display that behavior?
I finally understand how the .create() and .update() work in Serializer (especially ModelSerializer) and how they are connected to Viewsets (especially ModelViewSet). I just want clarify the concept more clearly if someone comes to this question.
Basically, the 4 methods CRUD in ModelViewSet: .create(), .retrieve(), .update(), and .destroy() will handle the calls from HTTP verbs. By default, the .create() and .update() from ModelViewSet will call the .create() and .update() from ModelSerializer by calling the .save() method from the BaseSerializer class.
The save() method will then determine whether it will call .create() or .update() in ModelSerializer by determining whether the object self.instance exists or not.
In the rest-api design create, read, update and delete is a standard.
There is not quite big difference in create and update.
Please refer to this and
see create() method will create an item.
and
update() method need to specify which item to be updated.

How to customize Django inline admin forms based on the object instance

The Django documentation makes it clear how to customize ModelForm instances based on the attributes of the particular Model instance being edited. However, I am working with a design that involves a lot of foreign key relationships between models, and I keep running into situations where I would like to modify a particular inline form instance based on the inline Model associated with it. I have dug through the documentation and the code, but the solution for this is eluding me.
The closest thing to a hook for this that I've been able to find is InlineModelAdmin.get_formset(), but the object instance that gets passed to that method is the parent object, not an instance of the child object. My instinct is that there is a way to do this, though. Does anybody know the proper way?
I am not 100% sure I fully understand what you are asking, but you can specify a forms.ModelForm for the admin inline (https://docs.djangoproject.com/en/1.7/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin.form) and that receives an instance of the current inline object and then you can change the form fields based on the instance.

Django - get instances of base model as corresponding proxy models

I have a base Django model, and proxy models that subclass it. They override all methods. I need to iterate over all the instances of the base model (i.e. for i in BaseModel.objects.all()), but be able to call the methods of their corresponding proxy classes instead of the placeholder methods declared in the base class.
How do I approach this? I actually have a model field which can determine which proxy model corresponds to each particular instance. Maybe I can make use of it and cast the base class into the subclass somehow? I'm at a loss.
EDIT: I've had a look at this question and have managed to change the class by writing to self.__class__. However, is that safe to use with Django?
proxymodels = {"Foo": FooModel, "Bar": BarModel}
for o in BaseModel.objects.all():
proxymodels[o.type].method_name(o, *args, **kwargs)
The methods are called on the proxy models (the classes), passing the BaseModel instances as first argument plus any additional arguments you want to pass. That way the methods are called as if they were called on an instance of a proxy model.
PS: re-assigning self.__class__ seems very hackish to me.

Categories