Django ORM and displaying many to many fields in template - python

I am having trouble displaying data from a many-to-many field on my template.
My models looks like so:
class User(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
email = models.EmailField()
password = models.CharField(max_length=255)
class Secret(models.Model):
message = models.TextField(max_length=1000)
posted_by = models.ForeignKey(User)
all_likes = models.ManyToManyField(User, related_name="all_users")
objects = SecretManager()
When a user clicks a like button, the user is associated with the secret via the all_likes field.
In my template, I am displaying all secrets, then a "delete" button if the session ID is equal to the posted_by user ID. Now all I'm trying to do is add the text "you liked this" if the secret's all_likes contains the user, but nothing i'm doing is right.
<table>
{% if secrets %}
{% for secret in secrets %}
<tr>
<td>{{secret.message}}</td>
<td>{{secret.created_at}}</td>
{% if request.session.id == secret.posted_by.id %}
<td>You posted this</td>
<td> <form action="{%url 'secrets:delete_secret' id=secret.id %}" method="POST">
{% csrf_token %}
<input type="submit" name="delete" value="delete">
</form>
{% endif %}
{% if request.session.id != secret.posted_by.id %}
<td> <form action="{%url 'secrets:create_like' user_id=request.session.id secret_id=secret.id %}" method="POST">
{% csrf_token %}
<input type="submit" name="Like" value="Like">
</form>
{% endif %}
////HERE I WANT TO ADD LOGIC TO DiSPLAY "YOU LIKED THIS" IF REQUEST.SESSION.ID IS EQUAL TO SECRET.ALL_LIKES.USER.ID////
</td>
</tr>
{% endfor %}
{% endif %}
</table>
How do I do this seemingly simple task?

Add a custom method in Secret class and decorate it as a property as below
#property
def all_like_ids(self):
return [x.id for x in self.all_likes]
Then in your template do :
{%if request.session.id in secret.all_like_ids %}
{%endif%}

Related

Trying to add a "add to wishlist button" in my Django web application

I'm trying to add an "add to wishlist" button to my page that lists books.
When a user presses the button, it will add the isbn (pk of the books) along with the user id into the wishlist table.
For some reason, it's not adding them into my database when I press the button. There are no errors shown so i don't know what's the problem.
Here's my code
#views.py
class wishlistView(TemplateView):
template_name = 'TextSearch/wishlist.html'
def post(self, request, pk):
isbn = self.request.POST.get('isbn')
current_user = request.user
book = wishlist(userid_id = current_user.id, ISBN_id = isbn)
book.save()
return
my HTML code
{% if user.is_authenticated %}
<form method="post">
{% csrf_token %}
<input type="hidden" value="{{book.pk}}" name="isbn">
<a href="{% url 'TextSearch:wishlist' book.pk %}" >Add To WishList</a>
</form>
{% else %}
<a href="{% url 'User:RegisterView' %}" >Add To WishList</a>
{% endif %}
my table
#models.py
class wishlist (models.Model):
userid = models.ForeignKey(User, on_delete= models.CASCADE)
ISBN = models.ForeignKey(books, on_delete = models.CASCADE)
Thank You in advance, I'm new to Django.
I personally use this method
MY MODEL:
class UserWishlist(models.Model):
user = models.ForeignKey(UserModel,on_delete=models.CASCADE,blank=False)
products = models.ManyToManyField(Product)
than in my view:
product = Product.objects.get(id=id)
obj, created = UserWishlist.objects.get_or_create(user=request.user)
if product in obj.products.all():
obj.products.remove(product)
else:
obj.products.add(product)
return JsonResponse({'msg':'Item added to wishlist!'})
much easier IMO and helps in toggling too
-- issue is fixed --
it was just a HTML mistake here's the updated code
{% if user.is_authenticated %}
<form action="{% url 'TextSearch:wishlist' book.pk %}" method="post">
{% csrf_token %}
<input type="hidden" value="{{book.pk}}" name="isbn">
<input type="submit" value="Add to wishlist">
</form>
{% else %}
<a href="{% url 'User:RegisterView' %}" >Add To WishList</a>

Pass object id to view from form submission

I have a website where user submitted entries are displayed and have an upvote count along with a button to upvote it yourself. These are represented with the Entry model. How do I pass the id of the particular Entry instance the user upvotes to a view?
html:
{% for entry in entries %}
<div id="upvote_count">
<form action="upvote" method="GET">
{% csrf_token %}
{{ entry.upvotes }}
<button id="upvote" type="submit">Upvote</button>
</form>
</div>
{% endfor %}
models.py:
class Entry(models.Model):
#...
upvotes = models.IntegerField( blank = True, null=True)
current url:
url(r'^upvote$', views.upvote),
You can pass it inside a hidden input, such as:
<form ...>
...
<input type="hidden" name="id" value="{{ entry.id }}">
<button ...>
</form>
and access to it in your view:
def upvote(request):
entry_id = int(request.GET.get("id"))
...

Django Foreign Key class not showing

The goal is to have a dashboard show a list of users in your area. The list of users works and it shows the Username. The only issue is I can't get the users images (ideally just have 1st image) to show. There are no error messages currently. Just nothing appearing.
models.py
class Images(models.Model):
image = models.ImageField(upload_to='profile_image', null=True, default='profile_image/none/no-img.png')
user = models.ForeignKey(User, on_delete=models.CASCADE, null=False)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True, null=True)
birth_date = models.DateField(null=True, blank=True)
...
views.py
class DashboardView(TemplateView):
template_name = 've/cp/dashboard.html'
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(DashboardView, self).dispatch(*args, **kwargs)
def get(self, request, pk=None):
users = User.objects.exclude(id=request.user.id)
try:
favorite = Favorite.objects.get(current_user=request.user)
favorites = favorite.users.all()
except Favorite.DoesNotExist:
favorites = None
args = {
# 'users': users, 'favorites':favorites, 'images': images,
}
return render(request, self.template_name, args)
dashboard.html
<div class="col-12">
<h2>People near you</h2>
{% for user in users %}
<a href="{% url 've:view_profile_with_pk' pk=user.pk %}">
<!--THIS WORKS-->
<h4>{{ user.username }}</h4>
<p>{{ user.profile.bio }}</p>
<!--But not this... why..-->
<p>{{ user.images.image.url }}</p>
'''
Or this.. However it does work on view_profile page where
there is a pk. Seem like it's not finding images for users,
as this results in a "No images message on localhost
'''
{% if images %}
{% for img in images %}
<a href="{{ user.img.image.url }}" target="_blank">
<img src="{{ user.img.image.url }}" class="" style="max-width: 300px">
</a>
{% endfor %}
{% else %}
<p>No images</p>
{% endif %}
</a>
<!-- Favorites works great -->
{% if not user in favorites %}
<a href="{% url 've:change_favorites' operation='add' pk=user.pk %}">
<button type="button" class="btn btn-success">Add Favorite</button>
</a>
{% endif %}
{% endfor %}
</div>
You can get user's images direcly from user object using images_set attribute.
In your case you can do something like this:
{% for user in users %}
{% with first_image=user.images_set.first %}
{% if first_image %}
<a href="{{ first_image.image.url }}" target="_blank">
<img src="{{ first_image.image.url }}" class="" style="max-width: 300px">
{% endif %}
{% endwith %}
{% endfor %}

How to apply upvote functionality with corresponding Model in Template?

I am developing a quora like app in Django but facing two problem. I want, in Question DetailView if user have not upvoted the answer then it will show the option of upvote and the total vote.
Here is my html template.
{% extends 'base.html' %}
{% block title %} {{ question.title }} {% endblock %}
{% block body %}
<h3><i>{{ question.title }}</i></h3>
{{ question.description }}
{% for answer in question.answer_set.all %}
<br>
<div>
<p>{{ answer.answer_text }} <span style="font-size: 15px">by [{{ answer.user }}]</span></p>
<form method="post" action="{% url 'answer:detail' question.id %}">
{% csrf_token %}
<input type="hidden" name="type" value="vote">
<input type="hidden" name="answer_id" value="{{ answer.id }}">
{% for upvote in answer.upvote_set.all %}
{% if upvote.user == request.user %}
<input type="submit" class="btn btn-sm btn-warning"
value="Downvote|{{ answer.upvote_set.all.count }}">
{% else %}
{% if forloop.counter == 1 %}
<input type="submit" class="btn btn-success" value="Upvote|{{ answer.upvote_set.all.count }}">
{% endif %}
{% endif %}
{% empty %}
<input type="submit" class="btn btn-sm btn-success" value="Upvote|{{ 0 }}">
{% endfor %}
</form>
{% endfor %}
{% endblock %}
models.py
from django.contrib.auth.models import User
from django.db import models
from django.shortcuts import reverse
# Create your models here.
class Question(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=200, blank=True, null=True)
description = models.TextField()
pub_date = models.DateTimeField()
def __str__(self):
return self.title
class Answer(models.Model):
answer_text = models.TextField()
questions = models.ForeignKey(Question)
pub_date = models.DateTimeField(auto_now_add=True)
# upvote = models.IntegerField(null=True, blank=True)
user = models.ForeignKey(User)
def __str__(self):
return self.answer_text
def get_absolute_url(self):
return reverse('answer:answer_by_me', kwargs={'pk': self.user.id})
class Upvote(models.Model):
user = models.ForeignKey(User)
answers = models.ForeignKey(Answer)
def __str__(self):
return self.user.username
It's showing both upvote and downvote button if user and any other user voted already.
I can't use break keyword if user's vote found in the answer.upvote_set.all even I can't use membership operator like if request.user in answer.upvote_set.all because it returns upvote object queryset. How should i solve this?
You have to create a custom tag for that.
Create a file in templatetags folder of your app named upvote_tags.py.
from django import template
register = template.Library()
#register.simple_tag
def has_upvoted(user, answer):
return user.id in list(answer.upvote_set.all().values_list("user", flat=True))
Now in your template you can use it like.
# load tag at top of your html.
{% load upvote_tags %}
...
# remove your loop and use condition as below
{% if has_upvoted request.user answer %}
# show downvote button for this answer
{% else %}
# show upvote button for this answer
{% endif %}
You also have to make entry of your tag file in TEMPLATE list under libraries keyword in your settings.py.
TEMPLATES = [
{
...
'OPTIONS': {
'libraries':{
# make your file entry here.
'upvote_tags': 'app_name.templatetags.upvote_tags',
}
},
},
]

How to set a custom attribute to a model which can be used in the template with ModelForm in Django?

Is it possible to write some attributes to a model field which can be used later to differentiate the different fields in the template?
model.py
from django.db import models
class Person(models.Model):
first_name = models.CharField("i am the Label", max_length=30)
last_name = models.CharField("i am other Label", max_length=30, customattr="Custom")
forms.py
class PersonForm(ModelForm):
class Meta:
Person
template.html
<form action="" method="post">{% csrf_token %}
{% for field in form %}
{% ifequal field.customattr 'Custom' %} # HOW COULD THIS WORK?
<p>Hello world.</p>
{{ field }}
{% else %}
<p>This is not Custom</p>
{{ field }}
{% endifequal %}
{% endfor %}
<input type="submit" value="Submit" />
</form>
Any hints?
Not really possible; field in your template code is a form field, not a model field. I would shift the presentation logic from the model into the template, and do something like this:
<form action="" method="post">{% csrf_token %}
{% for field in form %}
{% if field.name == 'last_name' or field.name == 'another_field' %}
<p>Hello world.</p>
{{ field }}
{% else %}
<p>This is not Custom</p>
{{ field }}
{% endif %}
{% endfor %}
<input type="submit" value="Submit" />
</form>
(the == operator was added in Django 1.2)
I don't understand why yo would like to do this. If you would like to define custom html to your ModelForm field you can override it like this:
class PersonForm(ModelForm):
class Meta:
Person
first_name = forms.CharField(
required = True,
widget = forms.TextInput(attrs={'style':'width:100px;'},
)
Like this you can tell Django how you would like to render your html.
You can find more detail in the doc https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#overriding-the-default-field-types-or-widgets

Categories

Resources