Query M2M from Django template language - django

This might be a rookie problem but I cannot find a way around it.
I am trying to implement add recipe to favourites.
The view and model work properly as when I hit the button once it sends the request and adds the recipe to the user's favourites. Then when clicked again it removes it correctly from the database.
Alas now when i try to make visible on the template I ran into trouble with the template language.
I cannot find a way to check if the current user's profile has liked the paginated recipe.
I have the following class based list view
class Home(ListView):
model = Recipe
template_name = 'home.html'
paginate_by = 2
def get_queryset(self):
return Recipe.objects.order_by('id').prefetch_related('profile__recipecomment_set__recipe')
and the following Recipe model
class Recipe(models.Model):
title = models.CharField(
max_length=TITLE_MAX_LENGTH,
validators=(
MaxLengthValidator(TITLE_MAX_LENGTH),
MinLengthValidator(TITLE_MIN_LENGTH),
),
null=False,
blank=False,
)
profile = models.ForeignKey(
Profile,
on_delete=models.RESTRICT,
null=False,
blank=True,
editable=False,
)
favourites = models.ManyToManyField(
Profile,
related_name='favourite',
default=None,
blank=True,
)
the template.html is as follows, this is the code that doesn't work
{% for recipe in page_obj %}
{% if request.user.profile.id in recipe.favorites %}
<button>Recipe added to favourites</button>
{% else %}
<button>Add to favourites</button>
{% endif %}
{% endfor %}
the pagination works, everything else is working except that I cannot check if the user has added each recipe to his favourites or not.

At last I found an answer myself.
It might not be the best but it works.
In the Home view I added a static method that checks each item in the context object_list and adds an attribute that later can be used inside the template.
#staticmethod
def is_in_favourites(request, recipe):
if request.user.is_authenticated:
return True if recipe.favourites.filter(id=request.user.profile.id).exists() else False
return False
def get_context_data(self, *, object_list=None, **kwargs):
ctx = super().get_context_data(object_list=object_list, **kwargs)
for i in ctx.get('object_list'):
i.is_in_favourites = self.is_in_favourites(self.request, i)
return ctx

Related

Django Template Language: Create conditional for entire Model (not record by record)

Newbie problem. Per the following script in a template ...
{% if request.user not in Result %}
<p>You have no account in the system.</p>
{% endif %}
... the statement "You have no account in the system." is appearing 100 times on screen---because there are 100 records and therefore the condition is being checked 100 times.
Is there a way to modify the script so that the statement appears just once? Meaning, it checks the entire database once for evidence that request.user appears anywhere in the model in aggregrate (and not whether it appears in each of the 100 records in the database)?
Maybe there's an easier/better way to do this in views.py vs a template, but that's beyond my knowledge. Thank you. Below is the model called Result.
models.py
class Result(models.Model):
custom_user = models.ForeignKey(CustomUser, default=None,
null=True, on_delete=models.SET_NULL)
decision = models.ForeignKey(Decision, default=None,
null=True, on_delete=models.SET_NULL,
verbose_name="Decision")
vote_eligible = models.BooleanField(default=True)
vote = models.CharField(default="", max_length=100,
blank=True, verbose_name="Your
Vote:")
voted_already = models.BooleanField(default=False)
#staticmethod
def get_absolute_url():
return "/home"
def __str__(self):
return f"{self.custom_user}"
views.py
class VoteForm(LoginRequiredMixin, CreateView):
model = Result
form_class = VotingForm
template_name = 'users/vote_form.html'
def get_context_data(self, **kwargs):
context = super().get_context_data()
context["Result"] = Result.objects.all()
return context
forms.py
class VotingForm(forms.ModelForm):
class Meta:
model = Result
fields = ['decision', 'vote']
views.py
Since the requirement is to display whether the logged in user has account in 'Result' model or not. I have filtered the rows specific to the user. You can loop over the user_specific in your template. If user is present in 'Result' model 'user_specifc' will have elements. If user is not present in 'Result' table, 'user_specific' will be empty. In you template, you can check whether 'user_specific' is empty list or not.
class VoteForm(LoginRequiredMixin, CreateView):
model = Result
form_class = VotingForm
template_name = 'users/vote_form.html'
def get_context_data(self, **kwargs):
context = super().get_context_data()
context["Result"] = Result.objects.all()
context['user_specific'] = Result.objects.filter(custom_user=self.request.user)
return context
template.html
{% if not user_specific %}
<p>You have no account in the system.</p>
{% endif %}

How to avoid Conditional Expressions from Duplicating Items

I am trying to annotate a queryset so that a button can appear in the home page (Listview) what there are posts more than one and when there Posts are admin_approved=True
So far the I have reached when there items in the list view with designers related to each item and in the for each post there is a user, in the queryset it checks if there posts related to the designer and these posts should by approvedby_Admin=True so that the button appears.
The issue is that when a user has 2 posts one which is approved and another not approved, 2 items appears and duplication takes place in the homepage List view
I have tried to use .distinct() but it didn't work items are still duplicated
Here is the models.py
class Post(models.Model):
designer = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100, unique=True)
admin_approved = models.BooleanField(default=False)
here is the views
from .models import Item
from django.db.models import Case, When, BooleanField, Q
class HomeView(ListView):
model = Item
paginate_by = 12
template_name = "home.html"
ordering = ['-timestamp']
def get_queryset(self):
has_post = Case(
When(Q(designer__post__isnull=False) & Q(designer__post__admin_approved=True), then=True),
default=False,
output_field=BooleanField()
)
return super().get_queryset().annotate(has_post=has_post).distinct()
here is the template
{% for item in object_list %}
{{ item.title }}
{% if item.has_post %}
SHOW BUTTON
{% else %}
HIDE BUTTON
{% endif %}
{% endfor %}
here is the item model
class Item(models.Model):
designer = models.ForeignKey(
User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
In Conditional Expressions, you are not passing value when condition evaluates to True,
has_post = Case(
When(designer__post__isnull=False,
then=True
default=False,
output_field=BooleanField()
)
Read more at https://docs.djangoproject.com/en/3.0/ref/models/conditional-expressions/

How to check new posts since users last login Django

Intro: I have a 3 models user, post, group. User is able to make posts however each post has to belong to a group. Users have to choose from the existing groups for their posts. Users cannot add, delete, update group's.
Furthermore:
Users can become a member of groups and when they click on a certain group. They see all the posts in that group.
What I want When Users come on the home page they see posts that were added since the last time they logged in
My Models
class Post(models.Model):
user = models.ForeignKey(User, related_name='posts')
group = models.ForeignKey(Group, related_name='posts')
title = models.CharField(max_length=250, unique=True)
message = models.TextField()
created_at = models.DateTimeField(auto_now=True)
My Views
class Homepage(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super(Homepage, self).get_context_data(**kwargs)
context['object_list'] = Group.objects.all()
context['post_list'] = Post.objects.order_by("-created_at")
#What am I doing wrong in the below code
if self.request.user.is_authenticated():
new_posts = Post.objects.filter(created_at__gt(self.request.user.last_login))
context['new_posts'] = new_posts.count()
return context
In my templates I have
<div class="list-group">
{% for group in object_list %}
{{group.name}}
{% if new_posts > 0 %}
{{new_posts}} new
{% endfor %}
</div>
NameError: name 'created_at__gt' is not defined
The error is because of you've missed a Equal to symbol,
new_posts = Post.objects.filter(created_at__gt = self.request.user.last_login)
^^^ here

How to access extended User attributes in django template

I am trying to access extended field of User model in Django template but it doesn't work, there are my files:
models.py:
class Author(models.Model):
user = models.OneToOneField(User, related_name='user', on_delete=models.SET_NULL, null=True)
bio = models.TextField(
max_length=1400, help_text="Enter author biography.")
def __str__(self):
return self.user.username
def create_profile(sender, **kwargs):
user = kwargs["instance"]
if kwargs["created"]:
user_profile = Author(user=user, bio='my bio')
user_profile.save()
post_save.connect(create_profile, sender=User)
template:
{% extends "base_generic.html" %}
{% block title %}
<title>Author {{user}}</title>
{% endblock %}
{% block content %}
<h1>{{user}}</h1>
<h2>Bio:</h2>
<p>{{user.author.bio}}</p>
<div>
{%for item in user.author.blogs_set.all%}
<p>{{item.title}}</p>
<hr>
{%endfor%}
</div>
{% endblock %}
views:
class UserDetailView(generic.DetailView):
model = User
template_name = 'blogapp/user_detail.html'
I want to get access to the bio field through user.author.bio but nothing displays I have also tried user.bio is there any tricky way to get access to this field?
You set the related_name to:
class Author(models.Model):
user = models.OneToOneField(
User,
related_name='user',
on_delete=models.SET_NULL, null=True
)
But the related_name is the name to access the related Author from a User object (so the name of the relation in reverse). You thus should set it to author (or leave it blank), like:
class Author(models.Model):
user = models.OneToOneField(
User,
related_name='author',
on_delete=models.SET_NULL, null=True
)
By setting it to user, you could have accessed the Author object with user.user, but I strongly advise not to do this, since in the end, it will only result in code that is hard to understand. For Django it of course does not matter (given no two relations originating from User have the same name), but for programmers, it gives a wrong impression.

Error 'NoneType' object has no attribute '_default_manager' when I create a view of Model instance

I'm new in Django and I'm developing an app but I'm stuck in this Error.
First I want to apologize for my English since is not my first language and then I hope I'm in the right place to ask for little help.
I'm developing a website about Publication of articles. The page "Publication" return a list of the model instances - in my case all the publications in the db.
What I'm trying to do is to assign a url to each of the instances of my Model, such that the user can click on it and see the page which is populated with other relevant information of that instance.
To do this I'm using get_absolute_url in my template and DetailView in my view.
The list of all the instance works fine but when I click on each of the instance I run the error 'NoneType' object has no attribute '_default_manager' .
I google already and try to follow some guidelines here and there, and try to find solution in Django doc couldnt figure out the solution...
Here my code:
models.py
class Publications(MPTTModel):
code = models.CharField(max_length=50)
title = models.CharField(max_length=150)
date = models.DateField(null=True)
magazine = models.CharField(max_length=50)
country = models.CharField(max_length=30)
slug = models.SlugField(max_length=150)
img01 = models.ImageField(upload_to="photo", blank=True, null=True)
link = models.URLField(max_length=200, blank=True, null=True)
template = models.ForeignKey("Template", related_name="publications", null=True, blank=True)
parent = models.ForeignKey('self', null=True, blank=True, related_name='children')
control_field = models.CharField(max_length=15)
class Meta:
verbose_name_plural = "Publications"
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('mag-publication',args=(self.slug,))
publications.html
{% load mptt_tags %}
{% load sito_tags %}
{% full_tree_for_model myApp.Publications as publications %}
{% for publications,structure in publications|tree_info %}
{% if publications.img01 %}
<div id="title_publication_container">
{{ publications.magazine }}
</div>
{% else %}
....
{% endif %}
{% endfor %}
urls.py
urlpatterns = patterns('',
url(r'^(?P<id_page>\d+)/(?P<slug>[\w-]+)/$', pages,),
url(r'^(?P<id_page>\d+)/(?P<slug_page>[\w-]+)/(?P<id>\d+)/(?P<slug>[\w-]+)/$', projects,),
url(r'^mag-publication/(?P<slug>[\w-]+)/$', PublicationDetailView.as_view() , name='mag-publication'),
)
view.py
class PublicationView(object):
queryset = Publications.objects.all()
class PublicationListView(PublicationView, ListView):
paginate_by = 20
class PublicationDetailView(PublicationView, DetailView):
slug_field = 'slug'
The error show me this Debug informations:
AttributeError at /6/publications/*title_publication*/
**'NoneType' object has no attribute '_default_manager'**
Request Method: GET
Request URL: .../6/publications/*title_publication*/
Django Version: 1.5.4
Exception Type: AttributeError
Exception Value: 'NoneType' object has no attribute '_default_manager'
Exception Location: /home/gabriele/virtual_envs/virt2/lib/python2.7/site-packages/feincms/views/cbv/views.py in get_object, line 20
Python Executable: /home/gabriele/virtual_envs/virt2/bin/python
Maybe the problem has something to do with feincms as it said in the "Exception Location" but everything came out after I tried to work on the instance of the Model.
Thank you for your help.
You probably didn't add feincms.module.page to your INSTALLED_APPS as per the documentation. If you follow the traceback, the error appears in get_object() where it tries to access the page model.
Are you using an older FeinCMS version? Newer versions raise a warning in that case.
Well i don't know for feincms but in your views you have :
class PublicationView(object):
queryset = Publications.objects.all()
class PublicationListView(PublicationView, ListView):
paginate_by = 20
class PublicationDetailView(PublicationView, DetailView):
slug_field = 'slug'
First of all you don't need to set the slug field if his name is already 'slug'.
And an other thing :
You inherit from PublicationView in your PublicationDetailView, but the DetailView need a single object, just try like this in your url file :
url(r'^mag-publication/(?P<slug>[\w-]+)/$', DetailView.as_view(model=Publications) , name='mag-publication')
Off course don't forget to import DetailView and Publications model into your url file.
EDIT 20/08/2014
In your get_absolute_url method you use :
return reverse('mag-publication',args=({'slug':self.slug}))
If you want to use dict for params you must use :
return reverse('mag-publication',kwargs={'slug':self.slug})
And if you want to use args you must do :
return reverse('mag-publication',args=(self.slug,))
I solved part of the problem. I used a combination of get_absolute_url and DetailView.
I can see the perfectly the list of the publications in publication.html but when I click to one of them the app show me - in publications_detail.html - the detail of all the instances of the Model instead only one.
From publications in admin "view on site" it works but there's the same problem, show me all the instances together.
The question is how to catch only one instance. Follow the code :
models.py
class Publications(MPTTModel):
title = models.CharField(max_length=150)
slug = models.SlugField(max_length=150)
img01 = models.ImageField(upload_to="photo", blank=True, null=True)
template = models.ForeignKey("Template", related_name="publications", null=True, blank=True)
parent = models.ForeignKey('self', null=True, blank=True, related_name='children')
control_field = models.CharField(max_length=15)
class Meta:
verbose_name_plural = "Publications"
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('mag-publication',args=(self.slug,))
views.py
class PublicationsDetail(DetailView):
queryset = Publications.objects.all()
template_name = 'website/publications_detail.html'
urls.py
url(r'^mag-publication/(?P<slug>[\w-]+)/$', PublicationsDetail.as_view()
publications_detail.html
{% load mptt_tags %}
{% load sito_tags %}
{% full_tree_for_model napeApp.Publications as publications %}
{% for publications,structure in publications|tree_info %}
{% if publications.img01 %}
<div id="title_publication_container">
{{ publications.magazine }}
</div>
{% else %}
....
{% endif %}
{% endfor %}
In my case I just renamed meta to Meta and it solved.maybe it is related to sensitive capital class name