Add Users to ManyToManyField in Model? - django

I'm new to Django! so could be way wrong :)
I'm building an event app.
I'm trying to add/remove users to a 'host' list in my 'EventProfile' model from 'MyGuest', i basically want a different host list in each 'EventProfile', i'm using a class method.
I can add/delete each object to each specific EventProfile model in the admin but cant seem to do it on my site?
I keep getting this error "get() returned more than one Host -- it returned 2!"
Heres my code please help :)
Models.py
class MyGuest(models.Model):
users = models.ManyToManyField(User)
current_user = models.ForeignKey(User, related_name='add_myguest', null=True)
#classmethod
def make_myguest(cls, current_user, new_myguest):
myguest, created = cls.objects.get_or_create(current_user=current_user)
myguest.users.add(new_myguest)
#classmethod
def lose_myguest(cls, current_user, new_myguest):
myguest, created = cls.objects.get_or_create(current_user=current_user)
myguest.users.remove(new_myguest)
class Host(models.Model):
users = models.ManyToManyField(User)
current_user = models.ForeignKey(User, related_name='add_myguest', null=True)
#classmethod
def make_host(cls, current_user, new_host):
host, created = cls.objects.get_or_create(current_user=current_user)
host.users.add(new_host)
#classmethod
def lose_host(cls, current_user, new_host):
host, created = cls.objects.get_or_create(current_user=current_user)
host.users.remove(new_host)
class EventProfile(models.Model):
event = models.CharField(max_length=100)
greeting = models.CharField(max_length=100)
invitee = models.CharField(max_length=100)
description = models.CharField(max_length=100)
date = models.CharField(max_length=100)
start_time = models.CharField(max_length=100)
finish_time = models.CharField(max_length=100)
venue = models.CharField(max_length=100)
myguest = models.ForeignKey(MyGuest, related_name='add_myguest_profile', null=True)
host = models.ForeignKey(Host, related_name='add_host_profile')
def __str__(self):
return self.event
Views.py
def event_change_hosts(request, operation, pk):
new_host = User.objects.get(pk=pk)
if operation == 'add':
Host.make_host(request.user, new_host)
return redirect('events:event_myguests_profile')
elif operation == 'remove':
Host.lose_host(request.user, new_host)
return redirect('events:event_hosts')
urls.py
url(r'^event_hosts/(?P<pk>\d+)$', views.event_hosts, name= 'event_hosts'),
url(r'^connect_hosts/(?P<operation>.+)/(?P<pk>\d+)/$', views.event_change_hosts, name= 'event_change_hosts'),
template.py
{% for host in hosts %}
<div class="alert alert-info" role="alert">
<a href="{% url 'accounts:profile_with_pk' pk=user.pk %}" type="button"
class="btn btn-primary left"
class="btn btn-default btn-lg active">{{ host.username }}
<a href="{% url 'events:event_change_hosts' operation='remove' pk=user.pk %}">
<button type="button" class="btn btn-danger pull-right btn-s">Remove</button>
</a>
</div>
{% endfor %}

Related

Reviews showing on the reviewer's own profile page instead of the profile that it was written on

I am adding a simple review function on my Social platform Django project, where Users can write a review on another user's profile. But after posting the review, it's only showing on the profile page of the user that I'm currently signed in to.
This is my models.py
` class Review(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
reviewmsg = models.TextField(null=True, blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.reviewmsg)`
views.py
` def userProfile(request, pk):
user = User.objects.get(id=pk)
rooms = user.room_set.all()
reviews = user.review_set.all()
if request.method == 'POST':
review = Review.objects.create(
user=request.user,
reviewmsg=request.POST.get('reviewmsg')
)
return redirect('user-profile', pk=user.id)
context = {'user':user, 'rooms':rooms, 'reviews':reviews}
return render(request, 'pages/profile.html', context)`
excerpt from my profile page template
` <div>
{% for review in reviews %}
<div class="comment-sect" style="margin-top: 0.5rem;">
<div class="comment-photo" style="margin: auto;">
<a href="{% url 'user-profile' review.user.id %}">
<div class="profile-photo">
<img src="{{review.user.avatar.url}}">
</div>
</a>
</div>
<div class="comment-info" style="width: 300px;">
<small>#{{review.user}}</small>
<small>{{review.created|timesince}} ago </small>
<p style="margin-bottom:0;">{{review.reviewmsg}}</p>
</div>
</div><!--end of comment-sect-->
{% endfor %}
<div class="comment-form" style="margin-top: 0.5rem; text-align:center;">
<form method="POST" action="">
{% csrf_token %}
<div>Submit a Review
<input type="text" name="reviewmsg" placeholder="What do you think about this User?"/>
</div>
</form>
</div>`
EDITED
As mentioned by #lain Shelvington, I agree that I need to add another ForeignKey to my models. I tried updating the models.py to:
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Review(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
profile = models.ForeignKey(Profile, on_delete=models.SET_NULL, null=True)
reviewmsg = models.TextField(null=True, blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.reviewmsg)
and views.py to:
user = User.objects.get(id=pk)
rooms = user.room_set.all()
reviews = user.review_set.all()
if request.method == 'POST':
review = Review.objects.create(
user=request.user,
profile=user,
reviewmsg=request.POST.get('reviewmsg')
)
return redirect('user-profile', pk=user.id)
context = {'user':user, 'rooms':rooms, 'reviews':reviews}
return render(request, 'pages/profile.html', context)
but ended up with error:
ValueError at /profile/7/
Cannot assign "<User: celeste>": "Review.profile" must be a "Profile" instance

Condition not working for 2 different apps in a Django template

I have created a project with 2 apps:
1.Core for items model to be viewed in home page
2. Score with posts model
The home.html is mainly for listing items by designers, the designers/users might have posts as well so I am trying to show buttons for designers/userswho have posts and hide them if the users don't have posts.
I have added the code but the button for the designers/usersposts is still showing.
App No. 1 CORE
Core Models.py
class Item(models.Model):
designer = models.ForeignKey(
User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
Core views.py
class HomeView(ListView):
model = Item
paginate_by = 12
template_name = "home.html"
ordering = ['-timestamp']
# context_object_name = 'posts' (I have commented this part as it is from Score Models
when included the home page become 404 error)
Here is the Core URL
urlpatterns = [
path('', HomeView.as_view(), name='home'),
App No.2 Score
Score Models.py:
class Post(models.Model):
designer = models.ForeignKey(User, on_delete=models.CASCADE)
design = models.ImageField(
blank=False, null=True, upload_to=upload_design_to)
Home.html template
{% if post.designer %}
<a href="{% url 'score:user-posts' item.designer.username %}">
<button style="margin-top: 10px;text-transform: none;" button type="button"
class="btn btn-success btn-sm btn-block">
Check my posts</button>
</a>
{% else %}
Show Nothing
{% endif %}
I dont know what might the reason for now working I have used the same logic with other templates but didnt succeed in this trial
You can adjust this solution to your project.
model.py:
class User(models.Model):
user = models.CharField(max_length=50)
def __str__(self):
return str(self.user)
class Item(models.Model):
designer = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
def __str__(self):
return str(self.title)
class Post(models.Model):
designer = models.ForeignKey(User, on_delete=models.CASCADE)
design = models.CharField(max_length=15, blank=False, null=True)
def __str__(self):
return str(self.design)
views.py:
def myview(request):
msg = ''
qs1 = Item.objects.all().values_list('designer')
qs2 = Post.objects.all().values_list('designer')
for result in list(set(qs1) & set(qs2)):
msg += '<a href = "www.google.com"><button style = "margin-top: 10px;text-transform: none;" type = "button" ' \
'class ="btn btn-success btn-sm btn-block" > Check my posts : {0}</button></a><br>'.\
format(User.objects.get(pk=result[0]))
context = {'msg': msg}
return render(request, 'stack/home.html', context)
home.html
<body>
{% autoescape off %}
{{ msg }}
{% endautoescape %}
</body>

DeleteView not deleting

I have a problem in implementing DeleteView for a model that has a related model. When I try to delete a Task object nothing happens and it is not redirecting to the success_url. Noting happens. It just keeps on displaying the template.
here are the models:
class Project(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
date_created = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Task(models.Model):
name = models.CharField(max_length=50, default='New Model')
project = models.ForeignKey(Project, on_delete=models.CASCADE)
date_created = models.DateTimeField(default=timezone.now)
Here is my DeleteView Class:
class TaskDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Task
template_name = 'tasks/confirm_delete.html' # template for deletion
success_url ='/projects/'
# Test user permission
def test_func(self):
task = self.get_object()
if self.request.user == task .project.author:
return True
else:
return False
def get_success_url(self):
project = self.object.project
return reverse_lazy('tasks-listview', kwargs={'pk': project.id })
and my URL patterns:
urlpatterns = [
path('tasks/<int:pk>/list/', TasksListview.as_view(), name='tasks-listview'),
path('tasks/<int:pk>/delete/', TaskDeleteView.as_view(), name='task-delete'),
]
and here is my delete temple:
<form method=" POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class='form-group'>
<h4>Current Project: {{ object.project }}</h4>
<h4>Are you sure you want to delete task named {{ object.name }}? </h4>
</fieldset>
<div class="form-group">
<button class="btn btn-danger float-sm-right mr-1" type="submit">Yes, Delete</button>
<a class="btn btn-secondary float-sm-right mr-1" href="{% url 'task-detail' object.id %}">Cancel</a>
</div>
</form>
My delete view class contains:
def delete(self, request, *args, **kwargs):
self.get_object().delete()
messages.add_message(self.request, messages.SUCCESS, 'Entity removed with success.')
data = {'valid': True}
return JsonResponse(data)

Django - How to make an upvote button?

Currently logged in users can upvote x amount of times.
The idea of fixing it is to create a new model called Vote. And it will have foreignkeys for user and product. When someone goes to upvote, you check and see:
is there vote object with this user id and product id, and if there is, you don't allow him to upvote again; and if it is not, then you can go ahead and create that, and just increase total_votes on one.
But actually, i ran into it and cant figure it out and solve.
So, there is my models.py
from django.db import models
from django.contrib.auth.models import User
class Product(models.Model):
title = models.CharField(max_length=255)
pub_date = models.DateTimeField()
body = models.TextField()
url = models.TextField()
image = models.ImageField(upload_to='images/')
icon = models.ImageField(upload_to='images/')
votes_total = models.IntegerField(default=1)
hunter = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def summary(self):
return self.body[:100]
def pub_date_pretty(self):
return self.pub_date.strftime('%b %e %Y')
class Vote(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
voteproduct = models.ForeignKey(Product, on_delete=models.CASCADE)
And my views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from .models import Product, Vote
from django.utils import timezone
#login_required(login_url="/accounts/signup")
def upvote(request, product_id):
if request.method == 'POST':
product = get_object_or_404(Product, pk=product_id)
product.votes_total += 1
product.save()
return redirect('/products/' + str(product.id))
Updates:
class Vote(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
class Meta:
unique_together(('user', 'product'),)
views.py
if request.method == 'POST':
product = get_object_or_404(Product, pk=product_id)
try:
Vote.objects.create(user=request.user, product=product)
product.votes_total += 1
product.save()
except:
product.save()
return redirect('/products/' + str(product.id))
Also, how should i change my .html file? I really cant understand what is going on. Looking forward to hear from u.
{% for product in products.all %}
<div class="row pt-3">
<div class="col-2" onclick="window.location='{% url 'detail' product.id
%}';" style="cursor: pointer;">
<img src="{{ product.icon.url }}" class="img-fluid" />
</div>
<div class="col-7" onclick="window.location='{% url 'detail' product.id
%}';" style="cursor: pointer;">
<h1>{{ product.title }}</h1>
<p>{{ product.summary }}</p>
</div>
<div class="col-3">
<a href="javascript:{document.getElementById('{% url 'upvote'
product.id %}').submit()}"><button class="btn btn-primary btn-lg btn-
block" name="btn1" value="upvote"><span class="oi oi-caret-top">
</span> Upvote {{ product.votes_total }}</button></a>
</div>
</div>
<form id="get_redirect_url{{ product.id }}" action="{% url 'upvote'
object.id %}" method="POST">
{% csrf_token %}
<input type="hidden" />
</form>
well if i were you i would do it like that :
#step 1 change votes_total from integerfield to manytomany field
##models
class Product(models.Model):
#other fields goes here
votes_total = models.ManyToManyField(User, related_name="votes" ,)
#step 2 ; go to your views.py and copy paste the following :
from django.views.generic import RedirectView
class ProductVoteToggle(RedirectView):
def get_redirect_url(self, *args ,**kwargs):
obj = get_object_or_404(Product, pk=self.kwargs['pk'])
url_ = obj.get_absolute_url()
user = self.request.user
if user.is_authenticated():
if user in obj.votes_total.all():
# you could remove the user if double upvote or display a message or what ever you want here
obj.votes_total.remove(user)
else:
obj.votes_total.add(user)
return url_
#step 3 : go to urls.py and paste add the following :
urlpatterns = [
#other urls here
path('vote/<int:pk>',ProductVoteToggle.as_view() , name="upvote"),
]
#in your template remove that form and paste the following code :
<a href="{% url 'upvote' product.id %}>Click to vote<a/>
when you are done it should work if it didn't past the error here and i'll let you know what you are doing wrong
Your code doesn't use Vote at all. In upvote(), first try to create a Vote for the user and product:
try:
Vote.objects.create(user=request.user, product=product)
# consider using the name product instead of voteproduct
If it succeeds, increment the counter. If it fails, notify the user that they have voted already.
Obviously, you need a unique constraint in Vote:
class Meta:
unique_together = ('user', 'product')

Django: Current login user in a query list

model.py
class Question(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length= 200)
description = models.TextField()
location = models.CharField(max_length=150)
tags = TaggableManager()
time = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.title
class Like(models.Model):
user = models.ManyToManyField(User, related_name='likes')
question = models.ForeignKey(Question)
date = models.DateTimeField(auto_now_add=True)
likecount = models.IntegerField(default=0)
urls.py
url(r'^$', ListView.as_view(queryset= Question.objects.all(),
template_name="index.html")),
index.html
{%for question in object_list %}
{%for user_liked in question.like_set.all%}
{% if user.username in user_liked.user.all%} `<!--if current logged in user in Like model user m2m field-->`
<button type="button" class="btn btn-disable" name= {{question.id}}"id="request">Liked </button> <!--disable like button-->
{%else%}
<button type="button" class="btn btn-enable" name= {{question.id}}"id="request">Like </button> <!--enable like button-->
{%endif%}
{%endfor%}
{%endfor%}
Problem is if template tag block doesn't work.
It works, but like_set.all is not list of usernames, but list of users. Therefore check if user.username is in list of users fails. Check if user is in like_set.all.