Django - filtering RelatedManager _set in template? - django

We have a Django DetailView where we're displaying an an object (Site), along with all of it's related objects (Rooms).
Now, in the template, we could simply iterate over the RelatedManager set:
{% for room in site.room_set.all %}
do stuff
{% endfor %}
However, the problem for this is that this will pick up all related rooms to a site - however, we need to narrow this set down somewhat by another attribute (let's call it year) - and this attribute is stored in a Django session variable.
Currently, we're just using Room.objects.filter(site=some_site, year='2009') in the view code, and that's fine.
My question is more from curiosity - is there any way to use _set in the template, and still filter down or narrow the set?
Could you write a custom Model Manager to do this, so that _set will only ever return objects for the current year? Or is there some other way?
Cheers,
Victor

My question is more from curiosity - is there any way to use _set in
the template, and still filter down or narrow the set?
Not by default, since there's no way to pass arguments to the filter call.
If it's functionality that needs to be repeated variably, often, and related to the template: build a custom template tag or filter (which can accept arguments).
If it's functionality that's specific to a view, code it in the view.
If it's functionality that's the same across multiple views, build the function in a dry place.
For #3, there are so many factors that determine where this code should go that there's no general solution. You could import a function into your views? Use a model manager? A model instance method? context processor? etc.
Could you write a custom Model Manager to do this, so that _set will
only ever return objects for the current year? Or is there some other
way?
It looks like you can actually just by using a model manager for your reverse related model.
class RoomManager(models.Manager):
def current_year(self):
return self.get_queryset().filter(year=datetime.date.today().year)
for room in site.room_set.current_year():
...
Or just on the parent model:
class Site(models.Model):
...
def year_room_set(self):
return self.room_set.filter(year=datetime.date.today().year)

Related

Designing my own list in django and bootstrap

Hi I'm pretty new to django and bootstrap.
I would like to know if there is a way to create a custom list items.
For example, I have my User class in models, I would like to show all users in a list that each item contains user fields such as image, name, age.
The questions are:
How do I design a single list item and then reuse it for all items in the list?
How do I use the model and fill the list from its fields?
(example : for each user in users, set image to user.image, set text1 to user.name, set text2 to user.age)
Thanks for the helpers in advance!!
Use a ListView. It will provide a context variable for your template called object_list that will contain your list. There is absolutely no need to copy the values from the model objects into some intermediary item class. There never is.
from django.views.generic.list import ListView
class UserListView(ListView):
model = User # your user model
By default this will use the template yourapp/user_list.html but you can override it by setting the class variable template_name in the UserListView class.
There's not more to it. That's why Django rocks. Many newcomers make mistakes and complicate things because they don't realize how powerful Django really is and how little code you need to write.

Django-rest-framework serializer makes a lot of queries

Let's say I have this model:
class Place(models.Model):
....
owner = ForeignKey(CustomUserModel)
....
And I have this DRF serializer that returns a list of Places (the view calling it uses DRF's generics.ListAPIView class):
class PlaceSerializer(serializers.ModelSerializer):
owner = UserModelSerializer() # Gets only specific fields for a place owner
class Meta:
model = Place
The problem is, when the serializer gets a query that returns, let's say... 50 places, I can see (in connection.queries) that a query is being made for each owner foreign key relation, which sums up to a lot of queries. This of course has a big impact on performance.
Also important to mention is that for the view calling the serializer I had get_queryset() return only Places that are in a certain distance from a center point using a custom query. I used Django's extra() method for that.
I have tried using select_related and prefetch_related with the query mentioned above, but it doesn't seem to make any difference in terms of queries being made later on by the serializer.
What am I missing?
select_related will work as expected with serializers.
Make sure you're setting that in the 'queryset' attribute on the view if you're using the generic views.
Using select_related inside 'get_queryset' will work too.
Otherwise the only thing I can suggest is trying to narrow the issue down with some more debugging. If you still believe there's an issue and have a minimal example that'll replicate it then raise the issue as a ticket, or take the discussion to the mailing list.

Get an url pk in a generic RESTful view?

I'm trying to manage my REST API like that :
http://xxx/users/userid[0-9]+/projects/projectid[0-9]+/tasks/taskid[0-9]+/
So I can access the JSON easily in my website. But, the thing is, I defined my view classes using the REST framework generic views. For example, here is my UserDetail view :
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
But of course I don't want all my users to be displayed, I just want my user with the ID userid to be displayed. I don't really know how to do it, I tried
queryset = User.objects.filter(id=userid)
but of course userid is not defined... Any help please ?
Edit : just to clarify, here is the url that leads to this view :
url(r'^users/(?P<pku>[0-9]+)/$', views.UserDetail.as_view(
), name='user-detail'),
First of all, If you want to use class based views, you should read some more about them. They are thoroughly explained on Django's docs and you can read about the specific generics of the framework you're using on the REST framework docs too. I'm not saying that you haven't read those, just that you seem to be missing some basic concepts that are explained there.
Now, to the problem at hand, if you look at the doc's of the generic view you're extending, you can see that it represents the endpoints for a single instance, meaning that it won't act on all your model's instances (as you seem to assume).
Also, you can see that this view is built on top of a series of other classes, the more critical one being GenericAPIView. On there you can see two things:
The queryset class field in this context is meant for filtering the posible instances you can manipulate, not obtaining the specific instance described on your url.
The lookup_field is the field that defines which attribute from your model will be used for getting the actual instance. You should define this field to whatever field you're going to use on your url to identify your object (generally it's pk). It's also important to note that the url should include a keyword argument corresponding to this value.
Internally, the view will take care of calling the get_object method, which usese the lookup_field value to find the specific model, and then feed that object to the serializer and return the result back to the client.
DISCLAIMER: I've never used Django REST framework, I put this answer togheter by reading the relevants docs and based on my experience.
I guess you need this:
Resolve function (django 1.4)
Then in your view class method you can do:
temp1, args, kwargs = resolve(self.request.path)

How to handle transient data in Django model (or how to avoid a global variable)?

I have a tree of models defined in my Django app (e.g. 'a' is a top level model, which has many 'b's, which has many 'c's). I also have the Views/Templates that render these appropriately. For each one of these models I typically need to do a Database query based on the current logged in user.
For example, it's similar to how each user on stack overflow can mark a question with a star. If my model is the question, I would ask the model if the current user has this question starred and then render it appropriate in the template.
My first thought was to try to pass a parameter in the template (which I now know doesnt' work).
# template
{{ question.is_starred(request.user) }} # Can't work.
My second thought was to have some type of global variable (which I don't like on principle).
# model
class question (Models.model)
def _is_starred(self):
# Use a global variable to find out the current logged in user!
My third thought was to have the View tell the model the currently logged in user, but the trouble is, I have a tree of model objects and I think I'd have to load and set every model in the tree, even if I don't end up using them all. I assume that the objects are lazily loaded.
# view
def view_one_question(request, question_id):
q = Question.objects.get(pk=question_id)
q.SetCurrentlyLoggedInUser (request.user.id)
# !!!! But what about the other network of objects that the question has?
return render_to_response(...)
Any advice is appreciated. I'm new to Django and trying to start this project off with the best design possible.
Your first example is the right idea, but the wrong implementation. You can't pass parameters to methods in Django templates. The easiest way around this is a simple filter:
# yourapp/templatetags/somefile.py
from django import template
register = template.Library()
#register.filter
def is_starred_for_user(question, user):
return question.is_starred(user)
Then, in your template:
{% load somefile %}
{{ question|is_starred_for_user:request.user }}

how to find associated Django ModelForm given the Model

I have dozens of Models, each with ONE associated ModelForm (whose Meta.model refers to the Model in question).
E.g.
class FooModel(Model):
pass
class FooModelForm(ModelForm):
class Meta:
model = FooModel
# current approach using a classmethod
FooModelForm.insert_in_model() # does cls.Meta.model.form = cls
So, obviously, it's easy to find FooModel given FooModelForm. What I want is to know the best way to do the REVERSE: find FooModelForm when I am presented with FooModel or even the string "Foo".
Assume only one ModelForm for each model, although solutions that return multiple are fine.
My current approach is to stash the model in the form class (as shown above), but I'm interested in knowing better approaches especially ones that could compute it centrally (without the final line above).
EDIT: I've reviewed things like Django: Display Generic ModelForm or predefined form but I believe this is a simpler question than those. The Django admin code must do something along the lines of what I seek. But get_model equivalent for ModelForms? suggests that might be voodoo and that it would be best to just do dict['Foo']=FooModelForm or its equivalent to keep track of the association explicitly. Seems repetitious.
If you have under 20 forms, sounds like mapping out a dictionary is the easiest way. Django does this kinda thing internally too.
For ModelForms, django admin just creates them on the fly via modelform_factory, so there is no comparable method to get_model
I do see, your method is bullet proof, but requires a line in ever model def.
If you only have one ModelForm per model, you could potentially iterate through the ModelForm subclasses until you find your form.
find FooModelForm when I am presented
with FooModel or even the string
"Foo".
modelforms = forms.ModelForm.__subclasses__()
def get_modelform(model):
try:
return filter(lambda x:x.Meta.model == model, modelforms)[0]
except IndexError:
print "apparently, there wasn't a ModelForm for your model"
If you want to pull the ModelForm as a string, you'll need to make sure both
app_label and __name__ are correct, which means it will be easier to use get_model('app', 'model') in the function.
You could combine this with your method and automatically place an attribute on your models that point to its ModelForm.
Hook into the class_prepared signal at the top of your apps, find the corresponding ModelForm and attach it to your Model class.
Hope that helps or gives you some ideas.