Django order_with_respect_to reverse foreign_key - python

I got troubles on CBW with Django 1.8. I use CBW, and if possible I'd like to avoid using views at all. But I'm stuck in a attempt to order the results I have with default queries.
so, more infos and the example : working on django 1.8.3
Got a few models, bands, that can go touring on events, in different venues
myapp/models.py >
from django.db import models
class Band(models.Model):
name = models.CharField(max_length=512)
class Venue(models.Model):
name = models.CharField(max_length=512)
class Meta:
order_with_respect_to = 'VenueEvents'
class Event(models.Model):
datetime = models.DateTimeField()
name = models.CharField(max_length=512)
venue = models.ForeignKey(Venue, trough='VenueEvents')
band = models.ForeignKey(Band)
class Meta:
ordering ['venue', 'datetime']
myapp/view.py >
#Empty
myapp/url.py >
from django.conf.urls import url, patterns
from django.views.generic import DetailView
from myapp.models import Band
urlpatterns = patterns('',
url(r'^bands/(?P<pk>[\d]+)$', DetailView.as_view(model=Band), name='venue')
and then the template file directly :
template/myapp/venue_detail.html >
<html><body><ul><li>
{{ band.name }}
{% for event in band.event_set.all %}
{% ifchanged event.venue %}
{% if not forloop.first %}
</li><li>
{% endif %}
{{ event.datetime }}
{% endif %}
{{ event.name }}
{% endfor %}
</li></ul></body></html>
I want on that page to have all the venues a band toured at listed, and I want them ordered by event_set.datetime. Meaning the first Venue listed is the one where they last played at.
I tryied a few different things, all not working :
adding through parameters to the ForeignKey. did not work
trying to stick the logic in the template. did not work (and who would do that ? )
having in Venue : ordering ['event_set']
So, I'd really like to have a solution with no view.py, but only if it is possible. So, what would be the best idea to solve this ?
Maybe is there a simple way to add a new table to help me order those things ?
Maybe can i add a method on my class Venue that would accept a band as argument and would be the thing i can order at, except django doesn't allow to order thing by a method.
Maybe I have to override the objects manager to add this possibility, but is there an easy way to do this ?
Or is just the only solution to skip views-less CBW ?
Or is there any other solution ?

Seems like you are using order_with_respect_to wrong.
You should probably order_with_respect_to = 'venue' on Event instead.
Have a look at https://docs.djangoproject.com/en/1.8/ref/models/options/#order-with-respect-to to see how it works.

Related

Django,getting only unique values from related object

I wish I could render in my website list of most used tags,which I do.The problem is,displayed tags often repeat and I'm sure it would be much better if only unique tags were displayed.
In my model I have writen method unique_tags
class ArticleTag(models.Model):
article = models.ForeignKey('ep.Article', on_delete=models.CASCADE, related_name='tags')
tag = models.ForeignKey('ep.Tag', on_delete=models.CASCADE)
def unique_tags(self):
return self.objects.values_list('tag').distinct()
and when I tested it in python shell it works fine.But it doesnt render any tag.My template looks like this:
<div class="widget-tags">
<ul>
{% for prev_article in object.articles.all %}
{% for article_tag in prev_article.article.tags.all.unique_tags %}
<li>{{article_tag.tag.verbose_name}}</li>
{% endfor %}
{% endfor %}
</ul>
</div>
Where object is from model that makes relation with Article table and Field table,so with object.articles.all I've got all instances of articles that are related to specific field.I use detail view in my views.
So,my first question is,Is this valid approach? I mean,adding new method in model class, or perhaps I should add this in views?.Also I still not comfortable with django template language so maybe there is problem.And I know there is this filter in template like {{ some_object | function}} but I've read that it is good practice to keep as little logic in template as it is possible.
I would add logic for this on the Article level if you want to do this:
class Article(models.Model):
def unique_tags(self):
return Tag.objects.filter(articletag__article=self).distinct()
and then query this with:
{% for article_tag in prev_article.unique_tags %}
<li>{{article_tag.tag.verbose_name}}</li>
{% endfor %}
By using a .values_list, you will obtain the value of the primary key, so usually an int (or another primitive type given you defined a primary key yourself).
That being said, I think it is better to simply make sure that this can never happen, by adding a unique_together [Django-doc] constraint in the model:
class ArticleTag(models.Model):
article = models.ForeignKey(
'ep.Article',
on_delete=models.CASCADE,
related_name='tags'
)
tag = models.ForeignKey('ep.Tag', on_delete=models.CASCADE)
class Meta:
unique_together = ('article', 'tag')
So now you can simply not tag the same article with the same tag twice. So once this is enforced, you can simply use:
<!-- given unique_together is enforced -->
{% for article_tag in prev_article.tags.all %}
<li>{{article_tag.tag.verbose_name}}</li>
{% endfor %}
and you can then use .prefetch_related(..) to load all the related objects with a constant number of queries.

Django: Using backwards relationships in views.py

I'm trying to pass an object to my HTML template consisting of the parent object and all child objects that relate to it. For instance:
Model Chamber:
class Chamber(models.Model):
chamber_name = models.CharField(max_length=100)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
Has ChamberProperty:
class ChamberProperty(models.Model):
chamber = models.ForeignKey(Chamber, on_delete=models.CASCADE)
property_name = models.CharField(max_length=50)
property_value = models.CharField(max_length=100)
user_defined = models.BooleanField(default=True)
They are two separate models because the customer can add as many properties as they want to their chamber.
In my views.py
class ChambersView(generic.DetailView):
template_name = 'pages/chambers.html'
def get(self, request):
user = User.objects.get(username=request.user)
customer = Customer.objects.get(user=user)
chambers_list = list(Chamber.objects.filter(customer=customer))
try:
chamber_properties = list(ChamberProperty.objects.filter(chamber__in=chambers_list).order_by('id'))
except:
chamber_properties = "No properties"
form = ChambersFilterForm(request=request)
return render(request, self.template_name, {'filter_form':form, 'chambers_list': chambers_list, 'chamber_properties': chamber_properties})
Now, that does get me a list of all chambers, and a list of all chamber properties. Except they're not linked to each other. I'm not sure how to build a list of related objects. I read about backwards relationships just now, but I don't seem to grasp how to use them.
I tried the following:
chambers_and_props = Chamber.chamberproperty_set.all()
And I get the following error:
AttributeError: 'ReverseManyToOneDescriptor' object has no attribute 'all'
So I'm not quite sure how to use it. The threads I saw mentioned that a relationship in Django automatically add a reverse on ForeignKeys and the usage should be parent.child_set.all() with child in lower caps.
I get a ReverserManyToOneDescriptor object, but not sure how to turn that into a usable list I can pass on to my HTML template.
Any ideas as to what I'm doing wrong?
Your query does not work because you have not specified which Chamber you want to get the backwards relation for.
However, this is not the right approach. Presumably you want the ChamberProperty so you can list them against each Chamber in the template. So, you should follow the relationship there in the template - there's no need to query ChamberProperty separately in the view at all.
{% for chamber in chamber_list %}
{{ chamber.chamber_name }}
{% for property in chamber.chamberproperty_set.all %}
{{ property.property_name }} : {{ property.property_value }}
{% endfor %}
{% endfor %}
You are getting the error because you are trying Chamber.chamberproperty_set on the model Chamber. It will work on individual chamber instances:
You can do this in the view:
for chamber in chambers_list
properties = chamber.chamberproperty_set.all()
Or in the template:
{% for chamber in chambers_list %}
{{ chamber }}
{% for property in chamber.chamberproperty_set.all %}
{{ property }}
{% endfor %}
Then, in your view, you can use prefetch_related to reduce the number of SQL queries:
chambers_list = Chamber.objects.filter(customer=customer).prefetch_related('chamberproperty_set')

Access to models with a foreign key in the template

I have a profile model with a one-to-one relationship to the User model so I can access to both models in the templates tanks to the user variable like this:
template.html
{% if user.profile.phone == 1234567890 %}
Show something
{% endif %}
That works fine, the condition gives True and show something but I have too the models Property and User_Property, the User_Property model have as Foreignkey the ids from User and Property.
models.py
class Property(models.Model):
name = models.CharField(max_length=50, unique=True)
class User_Property(models.Model):
us = models.ForeignKey(User, related_name='up_us')
prop = models.ForeignKey(Property, related_name='up_prop')
So if I try to access to the User_Property model like this:
{% if user.user_property.prop == 1 %}
Show something
{% endif %}
I can't access it shows nothing like it was False even when it's True, I have tried with user.user_property.prop_id == 1 too. It is beacause the relationship with the Profile model was made with the OneToOneField and the relationship with User_Property was made with the ForeignKey field and I need to pass in the context the User_Property model?
And it is possible to access to Property model like if I use a JOIN SQL statement in the template? something like this:
{% if user.user_property.property.name == 'the name of the property' %}
Show something
{% endif %}
Sorry for the long Post but I tried to add all the need info.
EDIT: Ok if someone need something similar this is what I did to solve the problem.
Create a context_processor.py to return a instance of User_Property and add it to my settings.py in this way I can access to the instance in all my templates even if I don't pass it as context in the views.
context_processors.py
from App_name.models import User_Property
from django.contrib.auth.models import User
def access_prop(request):
user = request.user.id #add the .id to no have problems with the AnonymousUser in my logg-in page
user_property = User_Property.objects.filter(us=user).values_list('prop', flat=True) #this stores the list of all the properties for the logg-in user
return {
'user_property': user_property,
}
settings.py
from django.conf.global_settings import TEMPLATE_CONTEXT_PROCESSORS
TEMPLATE_CONTEXT_PROCESSORS += ('App_name.context_processors.access_prop',)
Then in the template check if the user have a especific property
template.html
{% if 'property name' in user_property %}
Show something
{% else %}
This is not for you
{% endif %}
To can check in especific for the name instead of the id just add to_field='name' in my prop field in the model User_Property like this: prop = models.ForeignKey(Property, related_name='up_prop', to_field='name').
From the docs
You should use the related_name that you set in the ForeignKey and the built-in methods of the relationships:
try this:
user.up_us.filter(prop__name="the name")
EDIT
for using the .filter(prop__name="the name") method you have to do it in a .py file.
Give this a try: {% if user.user_property.prop.id == 1 %}
You've set related_name in us = models.ForeignKey(User, related_name='up_us'), so you need to use it
{% if user.up_us.prop.name == 'the name of the property' %}
Show something
{% endif %}
This answer has a good explanation of how to use and what related_name for.
And try to exclude to much logic from templates.

Getting and displaying related objects in Django

I know this is simple, but I can't get my head around how to join some models together to display in my template in Django. I have "groups" that can have several "contacts".
So far I've got:
class Group(models.Model):
group_name = models.CharField()
class Contact(models.Model):
contact_name = models.ForeignKey(Group)
In my view, at first I assumed that simply getting my groups would also get any attached contacts, however that doesn't appear to be happening as expected:
def get_queryset(self):
groups = Group.objects.all()
return groups
I was expecting to do something like this in my template:
{% for group in groups %}
<h2>{{ group.group_name }}</h2>
{% for c in group.contact %}
<h3>{{ c.contact_name }}</h3>
{% endfor %}
{% endfor %}
This isn't working - what am I doing wrong? What is the correct query in my view to make sure the contact(s) for each group is getting retrieved?
Well, it looks like you've got some of your code from a different place so just so you can fully understand, you can do this in 2 different ways:
1) To access a related object of any kind, being a simple ForeignKey or ManyToMany you just need to go from the opposite model and use _set like this example:
class Group(models.Model):
group_name = models.CharField()
class Contact(models.Model):
contact_name = models.ForeignKey(Group)
{{ group.contact_set.all }}
2) You can set up a name different than the default _set changing Contact like this:
class Contact(models.Model):
contact_name = models.ForeignKey(Group, related_name='contacts')
So, related_name kwarg set a new name for you instead of the _set one:
{{ group.contacts.all }}
I hope I manage to make it clearer about simple access on models related objects.

Querying Many to many fields in django template

This may not be relevant but just wanted to ask,
IF an object is passed from views to template and in the template will i be able to query many to many fields
Models code:
class Info(models.Model):
xls_answer = models.TextField(null=True,blank=True)
class Upload(models.Model):
access = models.IntegerField()
info = models.ManyToManyField(Info)
time = models.CharField(max_length=8, null=True,blank=True)
error_flag = models.IntegerField()
def __unicode__(self):
return self.access
Views:
// obj_Arr contains all the objects of upload
for objs in obj_Arr:
logging.debug(objs.access)
logging.debug(objs.time)
return render_to_response('upload/new_index.html', {'obj_arr': obj_Arr , 'load_flag' : 2})
In template is it possible to decode the many to many field since we are passing the object
Thanks..
In general, you can follow anything that's an attribute or a method call with no arguments through pathing in the django template system.
For the view code above, something like
{% for objs in obj_arr %}
{% for answer in objs.answers.all %}
{{ answer.someattribute }}
{% endfor %}
{% endfor %}
should do what you're expecting.
(I couldn't quite make out the specifics from your code sample, but hopefully this will illuminate what you can get into through the templates)
It's also possible to register a filter like this:
models.py
class Profile(models.Model):
options=models.ManyToManyField('Option', editable=False)
extra_tags.py
#register.filter
def does_profile_have_option(profile, option_id):
"""Returns non zero value if a profile has the option.
Usage::
{% if user.profile|does_profile_have_option:option.id %}
...
{% endif %}
"""
return profile.options.filter(id=option_id).count()
More info on filters can be found here https://docs.djangoproject.com/en/dev/howto/custom-template-tags/

Categories

Resources