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

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

Related

Annotate individually each item in a Django Queryset

Objective
I'm trying to get a queryset that tells me if a user is subscribed to a given mailing list and being able to access that bool using mailing_list.is_subbed
Ideally we would have a queryset where each item has a annotated field "is_subbed" which is either True or False.
Context
For context, this view is going to serve a form with checkboxes that are checked/unchecked depending on the status of the user.
The page is accessible in incognito mode through a url that holds a token which contains 1) The email of the user and 2) The ID of the mail send record (which holds details like the mailing list it's been sent to, details below)
Question
In the current state, the is_subbed function is called only once on the first item, and the resulting bool is annotated to every item, I'd like it to run for each item in the queryset.
How can I do that ? For now if the first item returns True once fed to is_subbed, every checkbox is marked because the annotated field is_subbed is set to True on each item.
Code
Here is my current work :
Summary :
The view
Implementation of function and models used in said view
Snippet I use to access my results in jinja2
views.py
class PressSubscriptionManagementView(TemplateView):
template_name = "mailing_list/press_subscription_management.html"
def is_subbed(self, user: User, mailing_list: MailingList) -> bool:
"""
Check if the user is subbed to the mailing list
"""
return user_current_state(user, mailing_list).event_type == "sub"
def get_context_data(self, **kwargs) -> dict:
context = super().get_context_data(**kwargs)
email, send_record_id = token_valid(kwargs["token"])
if email and send_record_id:
context["user"] = User.objects.get(email=email)
# In the current state, is_subbed is only called once on the first
# item in the list. If this call returns True, every checkbox is
# checked. None otherwise.
context["press_subscription_list"] = \
def get_context_data(self, **kwargs) -> dict:
context = super().get_context_data(**kwargs)
email, send_record_id = token_valid(kwargs["token"])
if email and send_record_id:
context["user"] = User.objects.get(email=email)
# In the current state, is_subbed is only called once on the first
# item in the list. If this call returns True, every checkbox is
# checked. None otherwise.
context["press_subscription_list"] = \
MailingList.objects.filter(
mailing_list_type="PR"
).order_by("-id"
).annotate( # noqa
is_subbed=(
ExpressionWrapper(
Value(
self.is_subbed(
context["user"],
F('mailing_list__id')
),
output_field=BooleanField()
),
output_field=BooleanField()
)
)
)
return context
implementation of user_current_state :
def user_current_state(user, mailing_list):
"""Return user's most current state on the provided mailing list
Return the most recent event associated with this user in this
mailing list.
"""
try:
the_user = MailingListEvent.objects.filter(
Q(event_type=MailingListEvent.EventType.SUBSCRIBE) |
Q(event_type=MailingListEvent.EventType.UNSUBSCRIBE),
user=user, mailing_list=mailing_list).latest(
'event_timestamp')
return the_user
except MailingListEvent.DoesNotExist:
return MailingListEvent(
user=user, mailing_list=mailing_list,
event_type=MailingListEvent.EventType.UNSUBSCRIBE)
Implementation of MailingList and MailingListEvent :
class MailingListEvent(models.Model):
class EventType(models.TextChoices):
SUBSCRIBE = 'sub', 'inscription'
UNSUBSCRIBE = 'unsub', 'désinscription'
user = models.ForeignKey(User, on_delete=models.CASCADE)
mailing_list = models.ForeignKey(MailingList,
on_delete=models.CASCADE)
class MailingList(models.Model):
# This is a user-visible name that can change.
mailing_list_name = models.CharField(max_length=80, blank=False)
# This is the unique name that can't change. It just makes code a
# bit more readable than referring to the pk id.
mailing_list_token = models.CharField(max_length=80, blank=False,
unique=True)
And Finally the implementation of SendRecord :
class TopicBlogEmailSendRecord(models.Model):
slug = models.SlugField(max_length=90, allow_unicode=True, blank=True)
mailinglist = models.ForeignKey(MailingList, on_delete=models.PROTECT)
recipient = models.ForeignKey(User, on_delete=models.PROTECT)
In the template, I access the value like this :
{% for mailing_list in press_subscription_list %}
[...]
<input type="checkbox" name="{{ mailing_list.mailing_list_name }}"
value="{{ mailing_list.id }}" id="mailing_list_id_{{ mailing_list.id }}"
{% if mailing_list.is_subbed %} checked {% endif %} />
[...]
{% endfor %}
Instead of your is_subbed() method, you could use a Subquery, combined with a conditional expression.
For example, something like this:
def get_context_data(self, **kwargs) -> dict:
...
# subquery mailing list events for each of the user's mailing lists
events = MailingListEvent.objects.filter(
user=context['user'], mailing_list=models.OuterRef('pk')
).order_by('-event_timestamp')
# annotate using subquery
annotated_mailing_lists = MailingList.objects.annotate(
latest_event_type=models.Subquery(events.values('event_type')[:1]),
is_subbed=models.Case(
models.When(latest_event_type='sub', then=True),
default=False,
output_field=models.BooleanField(),
)
)
context["press_subscription_list"] = annotated_mailing_lists.filter(...)...
Notes:
This assumes your MailingListEvent model defines event_type and event_timestamp fields (which are missing from your example).
For clarity, I used When(latest_event_type='sub', ..., but you should probably use MailingListEvent.EventType.SUBSCRIBE instead of 'sub'.

django - 'ModelName' object is not iterable

I have a model class named TemplateImages that holds the reference to images in my media folder.
I want to display the images in a loop on a html file.
Here is my models.py code:
class TemplateImages(models.Model):
image = models.ImageField(upload_to='template_images') # template_images/en/chronological/en-chronological-resume-1.png.
type = models.CharField(max_length=40) # e.g. chronological, functional, combination, coverletter
language_code = models.CharField(max_length=7, choices=languages.LANGUAGE_CHOICES, default='en') # en.
def __str__(self):
return '{}'.format(self.image)
Here is my views.py code:
def templates(request):
...
language_pref = request.user.userprofile.language_preference
chronological_images = core_models.TemplateImages('chronological', 'en')
...
return render(request, 'templates/templates.html', {
...
'chronological_images': chronological_images,
...
})
Here is a screen shot of my db:
On my html page, I have placed the following django for loop to display the 25 images on the page:
{% for c_image in chronological_images %}
{{c_image.image|file_url|safe}}"
{% endfor %}
I receive the following error message:
'TemplateImage' object is not iterable
I have searched google, but cannot locate any useful examples.
Can someone explain what I have done wrong?
You've simply constructed a TemplateImages object, not run a query. Try this:
chronological_images = TemplateImages.objects.filter(
type='chronological',
language_code='en',
)
You only constructed a new TemplateImages object that you did not save to the database, and of course that single object is not iterable. If you want to perform a query, you should use TemplateImages.objects.filter(), like:
def templates(request):
# ...
language_pref = request.user.userprofile.language_preference
chronological_images = core_models.TemplateImages.objects.filter(
type='chronological',
language_code='en'
)
# ...
return render(request, 'templates/templates.html', {
# ...
'chronological_images': chronological_images,
# ...
})

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.

Django prefetch_related on generic view DetailView

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 %}

Categories

Resources