get_object_or_create in a Mixin for differents models - python

I have a Mixin that allow me update the objects that I have created already, thing is that I have too many models and each one with different fields, this Mixin when not found the object return a 404, I need when the object is not found return the form for create the object associated to predio_id object , I have tried with get_object_or_create, but with this method I have to pass each field. How can achieve that when the object is not found, return his corresponding empty form for create it?
class UpdateModelMixin(object):
def get_object(self):
return get_object_or_404(self.model,predio_id=self.kwargs['predio_id'])
and it's called to view like this one:
class ManejoGeneralUpdateView(UpdateModelMixin, UpdateView):
model = ManejoGeneral
form_class = FormManejoGeneral
success_url = '/'
template_name = 'manejo_finca/edit/manejo_general.html'
Note that the UpdateView that I wrote here is just one of almost 30 o 40 UpdateViews that I have because each UpdateView call a different form and template

It was pretty simple, since get_or_create return a tuple, just add [0] at the end of the query:
class UpdateModelMixin(object):
def get_object(self):
return Persona.objects.get_or_create(predio_id=self.kwargs['predio_id'])[0]

Related

Difference between queryset attribute and get_queryset() method in django?

I am learning class based views in Django. I was reading the Django documentation and read about queryset attribute and the get_queryset() method. When googled them I came across this answer.
I tried to replicate the result using my code:
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
return Question.objects.order_by('-pub_date')[:2]
class IndexView2(generic.ListView):
template_name = 'polls/index2.html'
context_object_name = 'latest_question_list2'
queryset = Question.objects.all()
In answer it is mentioned that when you set queryset, the queryset is created only once, when you start your server. On the other hand, the get_queryset method is called for every request.
But I was able to insert questions in database and they were available in the page index2.html without restarting, I was able to change the database and changes were reflected on the page index2.html after refreshing the page.
I further googled and found this link. In the DRF website, it is mentioned that queryset will get evaluated once, and those results will be cached for all subsequent requests.
Can you point where I am going wrong ? What link I am missing ?
A QuerySet is evaluated once, but the default implementation of get_queryset, will use queryset.all(), thus each time constructing a new queryset that will force reevaluation.
Indeed, the implementation of the .get_queryset(…) method [GitHub] works with:
def get_queryset(self):
if self.queryset is not None:
queryset = self.queryset
if isinstance(queryset, QuerySet):
queryset = queryset.all()
elif self.model is not None:
queryset = self.model._default_manager.all()
else:
raise ImproperlyConfigured(
"%(cls)s is missing a QuerySet. Define "
"%(cls)s.model, %(cls)s.queryset, or override "
"%(cls)s.get_queryset()." % {
'cls': self.__class__.__name__
}
)
ordering = self.get_ordering()
if ordering:
if isinstance(ordering, str):
ordering = (ordering,)
queryset = queryset.order_by(*ordering)
return queryset
THis thus means that we each time make a new "copy" of the QuerySet that will be evaluated. In case the queryset is not specified, it will look for the model attribute, and work with the _default_manager for that model.
If you specified an ordering attribute, than that means it will also order the queryset.

Create corresponding models on ViewSet save

I have a simple model that is serialized and created. The Viewset for this is as follows:
class OrderViewset(viewsets.ModelViewSet):
depth = 1
serializer_class = OrderSerializer
...
def perform_create(self, serializer):
serializer.save(user=self.request.user)
populate_ing(serializer)
Once the user saves and creates the model, I aim to shoot of and call 'populate_ing(xxx)' which takes the model (in this case an order) and creates a number of related objects using a foreign key relationship.
Is it possible to handle this on save? Believe, as above, by overriding the perform_create I should do so. And, most importantly, how can I access the model which has just been created?
For more explicit of what I am after, I would hope to do the following:
Create 'Order' using ViewSet above
Pass 'Order' (or its id, etc depending whats possible) to function populate_ing
populate_ing does its magic and creates other models
Return 'Order'
My serializer is as follows:
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = '__all__'
In a normal Djnago view with a form, I would handle it something to the effect of:
def view_create_order(request):
form = OrderForm(request.POST or None)
if form.is_valid():
new_order = form.save()
populate_ing(new_order)
context = {"form": form}
template = "order/order-update.html"
return render(request, template, context)
The created instance will be available in instance attribute, so it can be pass to the populate_ing() function as,
class OrderViewset(viewsets.ModelViewSet):
# depth = 1
serializer_class = OrderSerializer
...
def perform_create(self, serializer):
serializer.save(user=self.request.user)
populate_ing(serializer.instance)

Accessing particular instances in a DRF ListSerializer

I currently have this Django models I want to serialize:
class Result(models.Model):
...
routes = models.ManyToManyField(Route)
...
class Route(models.Model):
...
class Feature(models.Model):
result = models.ForeignKey(Result)
route = models.ForeignKey(Route)
description = models.TextField()
And the DRF serializers looks like:
class ResultSerializer(serializers.ModelSerializer):
...
route = RouteSerializer(many=True, required=False)
...
class Meta:
model = Result
fields = '__all__'
class FeatureField(serializers.CharField):
"""
Accepts text in the writes and looks up the correct feature for the reads.
"""
def get_attribute(self, obj):
# We pass the object instance onto `to_representation`, not just the field attribute.
return obj
def to_representation(self, obj):
try:
search_result = self.root.child.instance
# FIXME: this is the problem.
feature = Feature.objects.get(route=obj.id, search_result=search_result)
feature = feature.description
except Feature.DoesNotExist:
feature = None
return feature
class RouteSerializer(serializers.ModelSerializer):
description = FeatureField(required=False)
class Meta:
model = Route
fields = '__all__'
The problem I mean in the code is that this works when I'm using a ResultSerializer with just one instance, but if I want to serialize several instances in a list view for example, and I pass a queryset to the serializer, DRF applies a ListSerializer on top of it and now the self.root.instance is a list of the records, and I can't access the individual Results that call the nested RouteSerializer so I can't retrieve the correct Feature.
I jumped into DRF code and finally understood what was going on:
If you serialize just one instance with serializer = ResultSerializer(result), the serializer.instance contains only this single, particular result instance, and the nested serializers and fields can access it without problem using self.root.instance.
Now, if you serialize several instances, like the default list action does, what really happens is the following:
a call like serializer = ResultSerializer(queryset, many=True) is performed
having many=True in the arguments triggers the many_init() method from BaseSerializer, and this creates a single ResultSerializer with the queryset as instance, so serializer.instance is the queryset.
next it creates a single ListSerializer extending ResultSerializer and its instance again is the queryset.
What I got wrong is thinking that the ListSerializer would create separated ResultSerializers for each element in the queryset.
How I finally solved this is overriding the ResultSerializer.to_representation() method:
class ResultSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
# When we call Results with many=True, the serializer.instance is a list with several records,
# we can't know which particular instance is spawning the nested serializers so we add it here.
self._instance = instance
return super(ResultSerializer, self).to_representation(instance)
and finally consume it in the FeatureField like this:
class FeatureField(serializers.CharField):
"""
Accepts text in the writes and looks up the correct feature for the reads.
"""
def get_attribute(self, obj):
# We pass the object instance onto `to_representation`, not just the field attribute.
return obj
def to_representation(self, obj):
# If the root is a ListSerializer, retrieve the right Result instance using the `_instance` attribute.
try:
if isinstance(self.root, serializers.ListSerializer):
search_result = self.root.child._instance
else:
search_result = self.root.instance
feature = Feature.objects.get(route=obj.id, search_result=search_result)
feature = feature.pickup_instructions
except Feature.DoesNotExist:
feature = None
return feature

Filtering Objects in Class based view Django using Query parameters?

I am using Class-based Generic views Listview for listing all objects.
My views.py:
class PostsList(ListView):
model = Post
template_name = "index.html"
My Urls.py:
urlpatterns = [
url(r'^$',PostsList.as_view(), name = "home"),
]
This gives me a list of all the posts. Now I want to filter/sort posts based on certain fields of Post Model, say price. Do I need to write this myself? If yes Which method of PostsLists class do I override ? def get, def get_context ?
I see the get method for Listview defined as below. In it can I pass URL query-parameters as **kwargs directly or I have to overwrite the below method in my class.
def get(self, request, *args, **kwargs):
....
You can override the get_queryset method:
Keep a mapping of all the parameters that you can get in the url kwargs.
def get_queryset(self):
queryset = Post.objects.all()
if self.request.GET.get('price'):
queryset = queryset.filter(price=self.request.GET.get('price'))
return queryset
When using Django's class based views, avoid overriding get() or post() if possible. These methods do a lot, and if you override them, you may have to duplicate a lot of the built in functionality. There are normally more specific methods that you can override.
In your case, you can filter the queryset dynamically with the get_queryset method. You can access GET parameters with self.request.GET. For example:
class PostsList(ListView):
model = Post
def get_queryset(self):
"""Filter by price if it is provided in GET parameters"""
queryset = super(PostsList, self).get_queryset()
if 'price' in self.request.GET:
queryset = queryset.filter(price=self.request.GET['price'])
return queryset
If your url captures arguments, you can access them with self.args (positional) and self.kwargs (name based).
See the docs on dynamic filtering for more info.

Django ModelForm with User data in Generic View

I have a model with a foreign key to group (the other fields don't matter):
class Project(models.Model) :
group = models.ForeignKey(Group)
...
I have a model form for this model:
class AddProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ["group","another"]
In my urls, I am using this in a generic view:
(r'^$', create_object, {'form_class':AddProjectForm, 'template_name':"form.html", 'login_required':True, 'extra_context':{'title':'Add a Project'}}),
That all works, but I want to have the group field display only the groups that the current user belongs to, not all of the groups available. I'd normally do this by passing in the user to the model form and overriding init if I wasn't in a generic view. Is there any way to do this with the generic view or do I need to go with a regular view to pass in that value?
This is gonna look dirty, since the generic view instantiates the form_class with no parameters. If you really want to use the generic_view you're gonna have to generate the class dynamically :S
def FormForUser(user):
class TmpClass(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(TmpClass, self).__init__(*args, **kwargs)
self.fields['group'].queryset = user.group_set.all()
class Meta:
model = Project
fields = ['group', 'another']
Then wrap the create object view
#login_required # Only logged users right?
def create_project(request):
user = request.user
form_class = FormForUser(user)
return create_object(request, form_class=form_class, ..... )
My recommendation is to write your own view, it will give you more control on the long term and it's a trivial view.
No, you'll need to make a regular view. As can be seen by looking at the source code for create_object(), there's no functionality to pass in extra parameters to the modelform (in django 1.2):
http://code.djangoproject.com/svn/django/branches/releases/1.2.X/django/views/generic/create_update.py

Categories