How django gets values of serializer fields? - django

I can't figure out how and where Django gets the value of the field which value is another serializer. I need to get a place where something like FirstSerializer(data) happens for the following example:
class FirstSerializer(ModelSerializer):
...
class SecondSerializer(ModelSerializer):
first = FirstSerializer()
...
class ThirdSerializer(ModelSerializer):
second = SecondSerializer()
...
class TripleView(APIView):
serializer_class = ThirdSerializer
...
Any thoughts?

Related

Extending a ModelForm with a ForeignKey field that was previously excluded

I have the following model:
class Watchman(models.Model):
group = models.ForeignKey('groups.Group')
name = models.CharField(max_length=64)
And the following model form:
class NewWatchmanForm(forms.ModelForm):
class Meta:
model = Watchman
fields = ['name']
At the time the new Watchman is created, I do not know which group it belongs to, hence name is the only model field defined in NewWatchmanForm. However, once I do figure out the correct group it belongs to, I use this form to update the object with the appropriate group:
class UpdateWatchmanForm(NewWatchmanForm):
group = forms.ModelChoiceField(queryset=Group.objects.all())
class Meta(NewWatchmanForm.Meta):
fields = NewWatchmanForm.Meta.fields + ("group",)
I was curious if there might be a better way to reuse my NewWatchmanForm. Ideally, I'd like to avoid having to declare the group explicitly, something like this:
class UpdateWatchmanForm(NewWatchmanForm):
# group = forms.ModelChoiceField(queryset=Group.objects.all())
class Meta(NewWatchmanForm.Meta):
fields = NewWatchmanForm.Meta.fields + ("group",)
However, if I do that, I get this error:
fields = NewWatchmanForm.Meta.fields + ("group",)
TypeError: can only concatenate list (not "tuple") to list
What is the correct way to subclass an existing ModelForm and include a ForeignKey field (without redefining forms.ModelChoiceField(...))?
I should have actually tried reading the error :|
The problem was that I was trying to concatenate a list with a tuple. Turns out that removing the group field in the subclass works just fine:
class UpdateWatchmanForm(NewWatchmanForm):
class Meta(NewWatchmanForm.Meta):
# Extend list with a list, not a tuple!
fields = NewWatchmanForm.Meta.fields + ["group"]

Change Document 'objects' property to 'query'

I'm trying to change the document 'objects' property to 'query'. It's more intuitive since one is querying the database.
Like;
Collection.query.find()
Instead of;
Collection.objects.find()
I have tried setting a query attribute to my Collection model like;
class Collection(Document):
def __setattr__(self, key, objects):
self.__dict__['query'] = self.objects
But on checking the type it returns a class of the QueryManager instead of Queryset like;
>>>print(type(Collection.query))
<'class' mongoengine.queryset.queryset.QueryManager >
Instead of;
>>>print(type(Collection.query))
<'class' mongoengine.queryset.queryset.Queryset >
Could someone offer a solution ?
Define an abstract Document class and within it define a custom QuerySet manager using queryset_manager wrapper. Inherit the abstract class as a Base class for all other subsequent Document classes.
from mongoengine.document import Document
from mongoengine.queryset import queryset_manager
class BaseDocument(Document):
meta = {'abstract': True}
#queryset_manager
def query(self, queryset):
return queryset
class Foo(BaseDocument):
...
To query use Foo.query.* which is more intuitive
instead of the default Foo.objects.*.
Displaying the type will return <class 'mongoengine.queryset.queryset.Queryset'> as expected.

Serializer needs to include more info in a GET while accepting primitives in a POST

Right now I have the following serializer:
class ShiftSerializer(serializers.ModelSerializer):
confirmed_location = LocationSerializer(required=False)
class Meta:
model = Shift
When I create a new Shift object I can pass along a basic object like this...
{date_requested: "2015-06-11", employee: 4, shift_requested: 2}
...and the object gets created without issue.
In an effort to make Shift objects a bit more verbose about who requested them, I declared a Serializer for the Employee field so that my GETs would include information about the employee:
class ShiftSerializer(serializers.ModelSerializer):
confirmed_location = LocationSerializer(required=False)
employee = EmployeeSerializer(required=False)
class Meta:
model = Shift
That produced the desired effect: now I can see who requested the shift without making a second query to the server for every single user. Unfortunately, after doing that I started getting HTTP 400 errors whenever I tried to POST that same object to make a new Shift:
Invalid data. Expected a dictionary, but got int.
I'm positive the EmployeeSerializer is freaking out because I'm not passing in an entire Employee object, only an id pointing to a particular Employee. Is it possible to include the entire Employee object when GETing Shift objects but still accept just a single integer when POSTing to create a new Shift object?
To get a read-only representation of the employee, you can add a SerializerMethodField. This will leave your employee field intact for POST and PUT requests and add a serialized representation at employee_data.
class EmployeeSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
class ShiftSerializer(serializers.ModelSerializer):
confirmed_location = LocationSerializer(required=False)
# this will look for a method named get_employee_data
employee_data = serializers.SerializerMethodField()
class Meta:
model = Shift
fields = (
'confirmed_location',
'date_requested',
'shift_requested',
'employee',
'employee_data',
)
def get_employee_data(self, obj):
""" Returns serialized representation of an employee.
"""
return EmployeeSerializer(obj.employee).data

Django queryset with Model method containing another queryset

Suppose I have a model, MyModel, with a property method that uses another model's queryset.
class OtherModel(models.Model)
...
class MyModel(models.Model):
simple_attr = models.CharField('Yada yada')
#property
def complex_attr(self):
list_other_model = OtherModel.objects.all()
...
# Complex algorithm using queryset from 'OtherModel' and simple_attr
return result
This causes my get_queryset() method on MyModel to query the database to generate the list_other_model variable every time for every single row.
Which causes my MyModel ListView to generate hundreds of SQL queries. Not efficient.
How can I architect a Manager or get_queryset method to cache the variable list_other_model for each row when using MyModel.objects.all()?
I hope my question makes sense--I'm on my sixth shot of espresso, and still haven't found a way to reduce the db queries.
Not sure if this is the best way to do it, but it works.
If someone posts a better answer, I'll accept theirs.
class OtherModel(models.Model)
...
class MyModelManager(models.Manager):
def get_queryset(self):
self.model.list_other_model = OtherModel.objects.all()
return super(MyModelManager, self).get_queryset()
class MyModel(models.Model):
simple_attr = models.CharField('Yada yada')
list_other_model = None
objects = MyModelManager()
#property
def complex_attr(self):
...
# Complex algorithm using queryset from 'OtherModel' and simple_attr
return result

How to pass parameters to django generic views

I would like to pass a number to my generic view (DetailView) to get one object Here is my code
Urlpattern
(r'^newreportview/(?P<number>\w+)/$', NewReportView.as_view()),
View Class
class NewReportView(DetailView):
template_name = "report/newreportview.html"
context_object_name = "newreportview"
def get_queryset(self):
task= get_object_or_404(MyTask,applicationnnumber=self.args[0])
return task
I guess something is wrong in this line
name = get_object_or_404(MyTask,applicationnnumber=self.args[0])
error message:
Exception Type: IndexError
Exception Value:
tuple index out of range
How should I pass 'number' to this generic view and get a Mytask object with this 'number'?
Thanks
You have missed the entire point of generic views. For a simple DetailView - which is a view of a single object - you just define the model and slug attributes in the class:
(r'^newreportview/(\d+)/$', NewReportView.as_view()),
class NewReportView(DetailView):
template_name = "report/newreportview.html"
model = MyTask
slug = 'applicationnnumber'
(You could also just as easily have passed those three as parameters in the URL definition, so no need to make a subclass at all.)
The reason why you were getting no values for self.args is that you had passed your parameter as a kwarg, not an arg. So self.kwargs['number'] would have worked, as would the revised URL I've shown here.