Displaying a many-to-many fields - django

I seem to have a problem displaying work orders. in my app. The clients does not have the same problem so why does the work orders not show up. Actually it is as almost as a black space appears rather than text that should appear from my database.
The problem seems to be because work orders have a many-to-many field. If I have {{work_orders}}instead of say `{{work_orders.description}}, I get this
<django.db.models.fields.related.ManyRelatedManager object at 0xa042c6c>
Here are some output from my app.
#views
#login_required
def invoice_details(request, id=1):
invoices_list = Invoice.objects.filter(pk=id)
client = invoices_list[0].client
work_orders = invoices_list[0].work_orders
return render_to_response(('invoice_details.html', locals()), {'work_orders': work_orders,'client': client, 'invoices_list': invoices_list}, context_instance=RequestContext(request))
#models.py
class Invoice(models.Model):
client = models.ForeignKey(Client)
date = models.DateField()
invoice_no = models.CharField(max_length=16)
work_orders = models.ManyToManyField(Work_Order)
contract_info = models.ForeignKey(Contract_Info)
def __unicode__(self):
return self.invoice_no
#invoice_details.html
{{client.company}}<br/>
{{client.address}}<br/>
{{client.city}}<br/>
{{client.postcode}}<br/>
{{work_orders.description}}<br/>
{{work_orders.quantity}}<br/>
{{work_orders.item_no}}<br/>

should it not be work_orders.all, e.g.
{% for work_order in work_orders.all %}
{{work_order.description}}
{% endfor %}

#views
from django.shortcuts import get_object_or_404
#login_required
def invoice_details(request, id=1):
invoice = get_object_or_404(Invoice, pk=id)
return render_to_response(('invoice_details.html', locals()), {'invoice': invoice}, context_instance=RequestContext(request))
#models.py
class Invoice(models.Model):
client = models.ForeignKey(Client)
date = models.DateField()
invoice_no = models.CharField(max_length=16)
work_orders = models.ManyToManyField(Work_Order)
contract_info = models.ForeignKey(Contract_Info)
def __unicode__(self):
return self.invoice_no
#invoice_details.html
{{ invoice.client.company }}<br/>
{{ invoice.client.address }}<br/>
{{ invoice.client.city }}<br/>
{{ invoice.client.postcode }}<br/>
{% for work_order in invoice.work_orders.all %}
{{ work_order.description }}<br/>
{{ work_order.quantity }}<br/>
{{ work_order.item_no }}<br/>
{% endfor %}

Related

In Django, is there a way you can limit a user to registering for an event only once?

I'm a newbie to Django, trying to build site to allow people to register for football matches.
At the moment, a user can register multiple times for the same match, which is obviously not ideal! Is there a way that I can identify if the currently logged in user has already registered, and then replace the register button with a message telling them that they have already registered? I guess I need some kind of Boolean value in my EventDetail view, and then I can make a conditional statement in the template, but I'm unsure as to how to implement this. I hope this question is clear, it's my very first post!
views.py:
class EventDetail(View):
def get(self, request, id, *args, **kwargs):
event = get_object_or_404(Event, pk=id)
registrations = Registration.objects.filter(event=event)
total_participants = 0
for person in registrations:
total_participants += 1
if person.guest:
total_participants += 1
remaining_spaces = event.max_participants - total_participants
template = "event_detail.html"
context = {
"event": event,
"total_participants": total_participants,
"registrations": registrations,
"remaining_spaces": remaining_spaces,
}
return render(request, template, context)
template
{% extends "base.html" %}
{% block content %}
<p>{{ event.title }}</p>
<p>{{ total_participants }} / {{ event.max_participants }} ({{ remaining_spaces }} spot{{ remaining_spaces|pluralize }}
remaining!)</p>
{% if total_participants < event.max_participants %}
Register
{% else %}
<p>This event has filled up.</p>
{% endif %}
<h2>Current Participants</h2>
<ul>
{% for person in registrations %}
<li>
{{ person.name }}
{% if person.guest %}
+1
{% endif %}
</li>
{% endfor %}
</ul>
{% endblock %}
models.py
from django.db import models
from django.contrib.auth.models import User
class Event(models.Model):
title = models.CharField(max_length=100)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name="event_posts")
event_date_and_time = models.DateTimeField()
venue = models.CharField(max_length=100)
max_participants = models.IntegerField()
extra_info = models.TextField(blank=True)
updated_on = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['event_date_and_time']
def __str__(self):
return self.title
class Registration(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="event_registration")
name = models.ForeignKey(User, on_delete=models.CASCADE)
ball = models.BooleanField(default=False)
bibs = models.BooleanField(default=False)
guest = models.BooleanField(default=False)
def __str__(self):
return str(self.name)

Django how to add a form to a DetailView with FormMixin

I am attempting to add a form for comments to a DetailView. The DetailView displays notes for specific projects. So the comments have a foreign key that is the specific note and the note has a foreign key for the specific project.
I am attempting to use FormMixin with DetailView. So far I have not bee successful. Currently I can get the form to display but it does not save and in the terminal I see the following error Method Not Allowed (POST): /projects/project/1/note/1/
I can get these to work separately but not with the form in the DetailView.
Here are my models:
class ProjectNotes(models.Model):
title = models.CharField(max_length=200)
body = tinymce_models.HTMLField()
date = models.DateField(auto_now_add=True)
project = models.ForeignKey(Project, default=0, blank=True, on_delete=models.CASCADE, related_name='notes')
def __str__(self):
return self.title
class ProjectNoteComments(models.Model):
body = tinymce_models.HTMLField()
date = models.DateField(auto_now_add=True)
projectnote = models.ForeignKey(ProjectNotes, default=0, blank=True, on_delete=models.CASCADE, related_name='comments')
The View:
class ProjectNotesDetailView(DetailView, FormMixin):
model = ProjectNotes
id = ProjectNotes.objects.only('id')
template_name = 'company_accounts/project_note_detail.html'
comments = ProjectNotes.comments
form_class = NoteCommentForm
def form_valid(self, form):
projectnote = get_object_or_404(ProjectNotes, id=self.kwargs.get('pk'))
comment = form.save(commit=False)
comment.projectnote = projectnote
comment.save()
return super().form_valid(form)
def get_success_url(self):
return reverse('project_detail', args=[self.kwargs.get('pk')])
The form:
class NoteCommentForm(forms.ModelForm):
class Meta:
model = ProjectNoteComments
fields =['body',]
widgets = {
'body': forms.TextInput(attrs={'class': 'form-control'})
}
The template:
% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="section-container container">
<div class="project-entry">
<h2>{{ projectnotes.title }}</h2>
<p>{{ projectnotes.body | safe }}</p>
</div>
<div><b>Comments on {{projectnotes.title}}</b></div>
{% if projectnotes.comments.all %}
{% for comment in projectnotes.comments.all %}
<div class="notecomments" style="padding: 10px;">
{{ comment.body | safe }}
</div>
{% endfor %}
{% else %}
<p>No comments have been have been added yet.</p>
{% endif %}
<h2>add note</h2>
<h1>Add Comment</h1>
<form action="" method="post">
{% csrf_token %}
{{ form.media }}
{{ form|crispy }}
<input type="submit" value="save">
</form>
{% endblock content %}
Try to change the order between DetailView and FormMixin in ProjectNotesDetailView then implement the post method (enabled by the `FormMixin):
class ProjectNotesDetailView(FormMixin, DetailView):
model = ProjectNotes
id = ProjectNotes.objects.only('id')
template_name = 'company_accounts/project_note_detail.html'
comments = ProjectNotes.comments
form_class = NoteCommentForm
def form_valid(self, form):
projectnote = get_object_or_404(ProjectNotes, id=self.kwargs.get('pk'))
comment = form.save(commit=False)
comment.projectnote = projectnote
comment.save()
return super().form_valid(form)
def get_success_url(self):
return reverse('project_detail', args=[self.kwargs.get('pk')])
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
Check how to use formmixin with detailview (documentation).

How to display sum of disctinct items in django?

I would like to display sum all the distinct categories of the products that belongs to the user. I searched on the web, but all the things that I tried doensn't work. You may find the models,view and template below. It doesn't give me anything at the html.
Model:
class Product(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,related_name='products')
category = models.CharField(max_length=120)
brand = models.CharField(max_length=120)
product = models.CharField(max_length=120)
price = models.DecimalField(decimal_places=2,max_digits=100)
class Comp_Product(models.Model):
product = models.ForeignKey(Product,on_delete=models.CASCADE, related_name="comp_products")
competitor = models.URLField()
price = models.DecimalField(decimal_places=2,max_digits=100)
change = models.FloatField()
stock = models.BooleanField()
last_update = models.DateField(auto_now_add=True)
View:
class DashboardList(ListView):
template_name='dashboard_by_user.html'
def get_queryset(self):
return Product.objects.filter(user=self.request.user).annotate(count_category=Count('category',distinct=True)).aggregate(sum_category=Sum('count_category'))
template:
{% for product in product_list %}
{{product.sum_category}}
{%endfor%}
welcome to stackoverflow,
I'm assuming that you want to count the number of distinct categories for the given user. This can be done like this:
views.py:
from django.views.generic.list import ListView
from .models import Product
class DashboardList(ListView):
template_name = 'dashboard_by_user.html'
def get_queryset(self):
return Product.objects.filter(user=self.request.user)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
Products = context['object_list']
context['distinct_category_count'] = Products.values(
'category').distinct().count()
return context
dashboard_by_user.html:
<h1>Products</h1>
<ul>
{% for product in object_list %}
<li>{{ product.brand }} - {{ product.product }} - {{ product.category }}</li>
{% empty %}
<li>No Producs yet.</li>
{% endfor %}
</ul>
<p> Number of distinct categories: {{ distinct_category_count }} </p>
This should give you an output similar to this one:

get() returned more than one Comment -- it returned 2

I was trying to add a new post and I got the error code in the title. It seems that when I click on a post with one comment it shows the detail page but if it has more than one I get the error. I think it has something to do with my detail view. I have been changing the kwargs in my comment variable and I have been getting different errors thats why I think it has to do with the detail view.
views.py
from django.shortcuts import render, get_object_or_404, redirect
from .models import Post, Comment
from django.utils import timezone
from .forms import PostForm
def post_index(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by("published_date")
return render(request, "blog/post_index.html", {"posts" : posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
comment = get_object_or_404(Comment, post=post.pk)
return render(request, "blog/post_detail.html", {"post" : post, "comment" : comment})
def post_create(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect("post_detail", pk=post.pk)
else:
form = PostForm()
return render(request, "blog/post_create.html", {"form" : form})
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=250)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="comments")
author = models.CharField(max_length=250)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
post_detail.html
{% extends 'blog/base.html' %}
{% block content %}
<div class="post">
{% if post.published_date %}
<div class="date">
{{ post.published_date }}
</div>
{% endif %}
<h2>{{ post.title }}</h2>
<p>{{ post.text|linebreaksbr }}</p>
</div>
<hr>
{% for comment in post.comments.all %}
<div class="comment">
<div class="date">{{ comment.created_date }}</div>
<strong>{{ comment.author }}</strong>
<p>{{ comment.text|linebreaks }}</p>
</div>
{% empty %}
<p>No comments here yet :(</p>
{% endfor %}
{% endblock %}
Don't use get_object_or_404, it would return only one comment, instead just filter out your comments by
comment.objects.filter(post=pk)

All the users name who liked a post in django

I cannot show all the users name who liked the post in django. I have tried many ways to do that but its not working.
my models.py:
class post(models.Model):
title = models.CharField(max_length=100)
image = models.ImageField(upload_to='post_pics', null=True, blank=True)
video = models.FileField(upload_to='post_videos', null=True, blank=True)
content = models.TextField()
likes = models.ManyToManyField(User, related_name='likes', blank=True)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def delete(self, *args, **kwargs):
self.image.delete()
super().delete(*args, **kwargs)
def get_absolute_url(self):
return reverse ('blog-home')
in views.py :
def like_post(request):
# posts = get_object_or_404(Post, id=request.POST.get('post_id'))
posts = get_object_or_404(post, id=request.POST.get('post_id'))
is_liked = False
if posts.likes.filter(id=request.user.id).exists():
posts.likes.remove(request.user)
is_liked = False
else:
posts.likes.add(request.user)
is_liked = True
return render(request, 'blog/home.html')
def post_likes(request, pk):
posts = get_object_or_404(post, pk=pk)
post_likes = posts.likes.all()
context = {'post_likes': post_likes,}
return render(request, 'blog/post_likes.html', context)
in urls.py:
path('post/<int:pk>/postlikes/', views.post_likes, name='post-likes'),
and in post_like.html:
{% extends "blog/base.html" %}
{% block content %}
{% for likes in posts %}
<p>{{ posts.likes.user.all }}</p>
{% endfor %}
{% endblock content %}
How can i see the usernames who liked the particular post?
You iterate over the post_likes, which are User objects, and then you render the user.username:
{% extends "blog/base.html" %}
{% block content %}
{% for user in post_likes %}
<p>{{ user.username }}</p>
{% endfor %}
{% endblock content %}
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.