Django prefetch_related on generic view DetailView - python

I have this mixin to apply prefetch_related on a view. This is the queryset that I am supposed to be working on:
MyMode.objects.all().prefetch_related('projects')
with these models:
class Workspace():
name = models.CharField(max_length=255)
class Project():
name = models.CharField(max_length=255)
workspace = models.Foreignkey(Workspace)
with this mixin I created:
class PrefetchRelatedMixin(object):
prefetch_related = None
def get_queryset(self):
if self.prefetch_related is None:
raise ImproperlyConfigured(u'%(cls)s is missing the prefetch_related'
"property. This must be a tuple or list." % {
'cls': self.__class__.__name__})
if not isinstance(self.prefetch_related, (tuple, list)):
raise ImproperlyConfigured(u"%(cls)s's select_related property "
"must be a tuple or list." % {"cls": self.__class__.__name__})
queryset = super(PrefetchRelatedMixin, self).get_queryset()
return queryset.prefetch_related(
", ".join(self.prefetch_related)
)
called in the view like this:
class WorkspaceView(DetailView):
prefetch_related = ['projects']
model = Workspace
def get_queryset(self):
return super(WorkspaceView, self).get_queryset()
However, when I try to iterate over the related objects in my template:
{% for p in object.projects %}
<li>{{ p.name }}</li>
{% empty %}
<li>No Projects in this Workspace</li>
{% endfor %}
I get this error:
'RelatedManager' object is not iterable
Is there something in the mixin that altered the object for it to be returning a RelatedManager?
Thanks in advance!

#danihp has it: you need the .all.
There is also an error here:
return queryset.prefetch_related(
", ".join(self.prefetch_related)
)
Should read
return queryset.prefetch_related(*self.prefetch_related)
prefetch_related takes multiple string arguments, not a single comma-separated string.

prefetch_related is the RelatedManager, you should perform method to get results:
In docs sample:
>>> pizzas = Pizza.objects.prefetch_related('toppings')
>>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
For your scenario:
{% for p in object.projects.all %}

Related

Automatically get name of the column with for loop in django template

I would like to automatically retrieve the name of the column with a for loop.
If I know the name of the columns I can write the template in this way:
<ul>
{% for co in team %}
<li>{{ co.name }}</li>
<li>{{ co.team}}</li>
<li>{{ co.sport}}</li>
{% endfor %}
</ul>
But in my present case, I do not know the names of the columns of my table. How can I display them?
Thank you
You can obtain the fields of a model with:
fields = Model._meta.get_fields()
This is a tuple of Field [GitHub] objects. These Field objects have a name attribute, so we can obtain the field names with:
from operator import attrgetter
field_names = map(attrgetter('name'), Model._meta.get_fields())
In case we do not know what the model is, we can obtain a reference to the model of a model instance with type(..):
fields = type(some_instance)._meta.get_fields()
All this logic however does not really belong in the Django template, but in the view (by design, Django templates are a bit restrictive to prevent writing business logic in the template). We can thus pass a list of field names to the template:
def some_view(request):
teams = Team.objects.all()
field_names = [f.name for f in Team._meta.get_fields()]
return render(request, 'some_template.html',
{'teams': teams, 'field_names': field_names}
)
If you however want to print the values of these teams, then this will still not work, since we can not easily get an arbitrary attribute in a template. Then we can again shift some processing to the view:
from operator import attregetter
def some_view(request):
teams = Team.objects.all()
field_names = [f.name for f in Team._meta.get_fields()]
atgetters = [attrgetter(fn) for fn in field_names]
rows = [[ag(team) for ag in atgetters] for team in teams]
return render(request, 'some_template.html',
{'teams': teams, 'field_names': field_names, 'data': data}
)
So here data is a list containing lists, such that data[i][j] contains the value for a field with name field_name[j] for teams[i].

How to pass django model field to function and have variables returned to context for use in template

I have some values that I collected from users in a form, which are saved to the DB. I want to take these values, perform an operation on them in function, and then return them so that I can use them in them alongside the other context.
Here is my model:
#models.py file
class MyModel(models.Model):
varA = models.PositiveIntegerField()
varB = models.PositiveIntegerField()
varC = models.PositiveIntegerField()
And here is the outside function that I am using:
#makecalc.py
def MakeCalc(varA, varB, varC):
#simplified from what I am actually doing
varD = varA*varB**varC
varE = varA+varB+varC
return varD, varE
And here is the views.py file:
#views.py file
from .makecalcs import MakeCalc
class MySummary(DetailView):
model = MyModel
def get_vars(self):
varD, varE = MakeCalc(varA, varB, varC) #Am I passing this correctly?
return varD, varE #How do I send these to context?
And finally the html:
<p> you input: {{ MyModel.varA }}</p>
<p> you input:{{ MyModel.varB }}</p>
<p> you input:{{ MyModel.varC }}</p>
<p> I output:{{ varD }}</p> #how do I call this?
<p> you input:{{ varE }}</p> #how do I call this?
So, my question is in the view, how do I add varD, varE to context so that I can use it alongside varA, varB, and varC from the model?
Add the get_context_data() method to your view, like this:
def get_context_data(self, **kwargs):
context = {}
context['your_custom_var'] = 'your_custom_value'
return super(MySummary, self).get_context_data(**context)

Django: Calling a model's function in my template - not working

I'm trying to call a function from my model (check_nick) in my template. It appears that the template is successfully hitting the function since the items in the function are printed. However I'm not getting the expected result (True) as the user.group I'm testing with is NICK which is part of the NICK_BRANDS list.
MODEL.PY:
NICK_BRANDS = ['NICK', 'NICKT', 'NICKN', 'NICKK', 'NICKA']
class User():
group = models.ForeignKey(Brand, null=True, blank=True)
def check_nick(self):
brand = self.group
print brand //prints NICK
print brand in NICK_BRANDS //prints False (should be True!)
if brand in NICK_BRANDS:
return True
else:
return False
TEMPLATE:
{% if user.check_nick %}
//add some markup
{% endif %}
Your debug prints some string representation of brand, but you are checking the actual object. Change your if-clause to sth like:
if str(brand) in NICK_BRANDS:
# if brand.title in NICK_BRANDS:
# if brand.name in NICK_BRANDS:
# or whatever field of Brand is "NICK"
self.group will be an instance of the related Brand model, not a string, and hence would probably not return True with the in statement. I presume there is some Brand.name property and you should be using:
def check_nick(self):
return self.group.name in NICK_BRANDS

How to make foreign key accept field value instead of its id in django?

I have created a checkbox for content filtering of products based on category.So when the user clicks on any checkbox only the books with that category should be shown.In the view I am passing the value of checkbox field(category name) obtained from the template but upon filtering, the foreign key is expecting pk(id) instead of field value.I am getting error like this,invalid literal for int() with base 10: '<category name>'.So is it possible to make foreign key accept value instead of id?
Models.py,
class Add_cat(models.Model):
category = models.CharField("Name",max_length=25,unique=True)
def __unicode__(self):
return u'{0}'.format(self.category)
class Add_prod(models.Model):
book = models.CharField("Book Name",max_length=40)
author = models.CharField("Author",max_length=30)
price = models.PositiveIntegerField("Price")
image = models.ImageField(upload_to='images',null=True)
cat = models.ForeignKey(Add_cat,on_delete=models.PROTECT)
Template file,
{% for i in products %}
<input type="checkbox" name="cat_name" value="{{i.cat}}">{{i.cat}}<br>
{% endfor %}
Views.py,
def welcome_user(request):
if 'cat_name' in request.GET:
filter_category = request.GET.get('cat_name')
my_products = Add_prod.objects.filter(cat__in = filter_category)
context = { "products":my_products}
else:
my_products = Add_prod.objects.all()
context = { "products":my_products}
return render(request,"welcome-user.html",context)
You can check in the category field itself:
my_products = Add_prod.objects.filter(cat__category__in=filter_category)
Have a look at the documentation on how this works.
Above, is only applicable if filter_category is a list. If it is a string you can filter like following:
my_products = Add_prod.objects.filter(cat__category=filter_category)
There are two things wrong with your code
You need to look up the field rather than the foreign key
By using __in you are looking the category is equal to any one of the characters in the filter_category.
Hence to fix, use the field lookup and remove the __in
Add_prod.objects.filter(cat__category=filter_category)
You can try this,it will help you:
Add_prod.objects.filter(cat__category = filter_category)

How to parse out {'value__avg': 46.26524716693248} 'value_avg' when using aggregate(Avg()) in django

I am using django here is my model:
class Location(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=50)
altitude = models.IntegerField(max_length=10000)
area = models.ForeignKey('Area')
def __str__(self):
return str(self.area) + ':' + str(self.name)
#measurement.id value date location
class Measurement(models.Model):
id = models.IntegerField(primary_key=True)
value = models.FloatField(max_length=50)
data = models.DateTimeField()
location = models.ForeignKey('Location')
def __str__(self):
return "measurement#"+str(Location.objects.filter(id=self.id))
My HTML page is showing {'value__avg': 46.26524716693248} when it should just show 46.265.
Heres my function:
#property
def average_measurement(self):
locations = Location.objects.filter(area__name=self.name)
return Measurement.objects.filter(location__in=locations).aggregate(Avg('value'))
so how do I get the ugly part out?
aggregate() returns a dictionary where the key is combined from the grouping keys and grouping function name, you can just get the value by key:
return Measurement.objects.filter(location__in=locations).aggregate(Avg('value'))["value__avg"]
Or, if needed, you can also do that in the template using the dot-notation:
{{ obj.average_measurement.value__avg }}
You can also preset the key name with your own value:
return Measurement.objects.filter(location__in=locations).aggregate(my_average=Avg('value'))
Then, you would access it as:
{{ obj.average_measurement.my_average }}
That's not the ugly part, it's expected output and you need to understand what does the output mean. When you do django Aggregation, it returns a dictionary-like object with your aggregation criteria as keys and results as values.
What you need to do is to access it like a dictionary in template to extract the values:
{% for item in items %}
{{ item.value__avg|floatformat:3 }}
{% endfor %}
Check django doc about what is the lookup sequence for dot in template.
Also checkout django doc about aggreate function call.
Also checkout django doc about floatformat template filter.

Categories

Resources