How to update my Django database with a submit button? - python

I am creating a web application that will serve as a grocery store. The way I set it up is so the customer can come onto the website, click on the items that they would like to purchase, and then click a submit button to purchase those items. The problem I am running into is having a views.py function to take the information of which products were selected and subtracting 1 from the quantity of the database.
"""models.py"""
class Post(models.Model):
title = models.CharField(max_length=100)
Price = models.DecimalField(max_digits=4, decimal_places=2,default=1)
Sale = models.DecimalField(max_digits=4, decimal_places=2,default=1)
quantity = models.IntegerField(default=1)
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = TreeForeignKey('Category',null=True,blank=True, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
views.py
class PostListView(ListView):
model = Post
template_name = 'blog/home.html' # <app>/<model>_<viewtype>.html
context_object_name = 'posts'
def inventory(request):
products = request.POST.getlist('products')
for product in products:
a = Post.objects.get(title=product)
a.quantity = a.quantity -1
a.save()
print(products) # This line isn't necessary but it will show what is in your list from the terminal.
return redirect('blog-home')
urls.py
path('user/<str:username>', UserPostListView.as_view(), name='user-posts'),
path('inventory', views.inventory, name='inventory'),
home.html
{% extends "blog/base.html" %}
{% block content %}
<form action="{% url 'inventory' %}" method="POST" id="menuForm">
{% for post in posts %}
{% if post.quantity > 0 %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2">{{ post.category }}</a>
</div>
<h2><a class="article-title" >{{ post.title }}</a></h2>
<p class="article-content"> Price: ${{ post.Price }}</p>
<p class="article-content"> Sale: ${{ post.Sale }}</p>
<input type="checkbox" id="product_{{ post.id }}" value="{{ post.title }}" form="menuForm" name="products" > Inventory count: {{ post.quantity }}
</input>
</div>
</article>
{% else %}
{% endif %}
{% endfor %}
<button type="submit" form="menuForm">Confirm Purchase</button>
</form>
{% endblock content %}
My goal is to click on the checkboxes, and when the customer clicks the button on the bottom of home.html, it triggers the inventory function to subtract "1" from the quantity. When I say print(products), then the terminal gives me the title of all the posts that are check-marked. However, it still isn't going into the database and subtracting 1 from the inventory. (I solved it)

Using Post.objects.get('products[x]') does not make sence, the .get(…) method [Django-doc] expects Q objects as positional parameters, or named parameters, so for example:
from django.shortcuts import redirect
def inventory(request):
products = request.POST.getlist('products')
Post.objects.filter(pk__in=products).update(
quantity=F('quantity')-1
)
return redirect('profile')
By using a .update(…) [Django-doc] you decrement all Posts in bulk which is more efficient than making two database queries per Post object.
Using HttpResponseRedirect("{% url 'profile'%}") will not work either. The "{% url 'profile'%}" is just a string, it does not perform any template rendering. Django will normally return a HTTP 302 response, with {% url 'profile' %} as link to go to, but the browser does not understand {% url 'profile' %}, it can only work with a URI.

Related

Posts not showing author who wrote it in Django

Basically, I'm writing an app in which people can make blog and image posts. So far, I've completed users to be able to write text posts. However, when I try to create a post, it returns "By: None" when it should be returning "By: shrey". In this case, Bob is the author. Here's an image:
Here's an image for the post creation view:
Theoretically, when I enter a post it should say who it was written by.
Here's the template for the create post:
{% extends "social/base.html" %}
{% load crispy_forms_tags %}
{% block content4 %}
<h1>Make Your Post</h1>
<p>Write a post / Share an image</p>
<br>
<div class="container">
<form method="post">
{% csrf_token %}
{{form|crispy}}
<button type="submit" name="button">Make Post</button>
</form>
</div>
{% endblock content4 %}
Here's the function for the create post view:
class PostCreateView(CreateView):
model = Posts
fields = ['post_title', 'post_text_content']
def form_valid(self, form):
form.instance.author = self.request.user
print(self.request.user)
return super().form_valid(form)
Thank you in advance.
EDIT: Home Page Template (template which displays the posts):
{% extends "social/base.html" %}
{% block content %}
<h1>Your Feed</h1>
<p>This is your feed. Here, you'll see posts from people you follow.</p>
{% if user.is_authenticated %}
<p>You are logged in as {{user.username}}. This is your feed.</p>
{% else %}
<p>You are not logged in. This is a random feed.</p>
{% endif %}
{% for post in posts %}
<h1>{{ post.post_title }}</h1>
<p>By {{ post.post_author }} on <i>{{ post.post_date }}</i></p>
<p>{{ post.post_text_content }}</p>
{% endfor %}
Click here to make a post.
<br>
Click here to logout.
<br>
Click here to login.
<br>
Click here to sign up and make an account.
<!--<p>Want to post something? Enter your info here: </p> -->
{% endblock content %}
Posts Model:
class Posts(models.Model):
post_title = models.CharField(max_length = 40, help_text = 'Enter post title')
post_text_content = models.TextField(max_length = 1000)
post_author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
post_date = models.DateField(auto_now = True, auto_now_add = False)
#Make optional Image Field
class Meta:
ordering = ['post_title', 'post_author', 'post_date', 'post_text_content']
def __str__(self):
return self.post_title
def get_absolute_url(self):
return reverse('social-home')
The name of the field is post_author, not author, hence you should set post_author:
class PostCreateView(CreateView):
model = Posts
fields = ['post_title', 'post_text_content']
def form_valid(self, form):
form.instance.post_author = self.request.user
return super().form_valid(form)
That being said, normally in Django one does not prefixes the model fields with the name of the model. One reason not to do that is that you can define abstract models where you define the field once, and then use inheritance to add the field to other models.

Redirected to same page after POST method django

I am creating this project in django. I am working on reporting films, where I go to a film-detail view, then hit the report button. A report_form shows up where I justify why I am reporting, and then I click report. Everything works fine, but there is one thing. After reporting I get sent back to a random(?) film-detail view, but I would like to go back to the view for the film I am reporting. But how???
views.py
class FilmReportView(LoginRequiredMixin, CreateView):
model = Report
fields = ['reason']
def form_valid(self, form):
form.instance.reporter = self.request.user
form.instance.reports_id = self.kwargs['pk']
return super().form_valid(form)
def get_success_url(self):
return "film/<int:pk>/report"
report_form.html
{% extends "board/base.html" %}
{% load crispy_forms_tags %}
{% load materializecss %}
{% block content %}
<div class="valign-wrapper row login-box">
<div class="col card hoverable s10 pull-s1 m6 pull-m3 l8 pull-l2">
<form method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
<div class="card-content">
<span class="card-title">Jusity why you want to report this film?</span>
<div class="row">
{{ form|materializecss }}
</div>
</div>
<div class="card-action right-align">
<input type="reset" id="reset" class="btn-flat grey-text waves-effect">
<input type="submit" class="btn green waves-effect waves-light" value="Report">
</div>
</form>
</div>
</div>
{% endblock content %}
urls.py
urlpatterns = [
path("", views.films_view, name="board-home"),
path("film/add", FilmAddView.as_view(), name="film-add"),
path("film/<int:pk>/", FilmDetailView.as_view(), name="film-detail"),
path("film/<int:pk>/report", FilmReportView.as_view(), name="film-report"),
]
models.py
class Report(models.Model):
reason = models.TextField()
reporter = models.ForeignKey(User, on_delete=models.CASCADE, related_name="Reporter")
reports = models.ForeignKey(Film, on_delete=models.CASCADE)
def __str__(self): # pragma: no cover
return f"{self.reports.title} reported by {self.reporter.username}"
def get_absolute_url(self): # pragma: no cover
return reverse("film-detail", kwargs={"pk": self.pk})
Fix your FilmReportView get_success_url() to look like that:
def get_success_url(self):
return reverse("film-detail", kwargs={"pk": self.object.reports.id})
That should take care of it
In your views.py or even in the template you can put next parameter with url you want:
form/action/url?next={{request.path}}
See more about next:
https://www.reddit.com/r/django/comments/32s9ag/how_do_i_set_the_next_context_variable_in_django/

How to display post and related comments on single page?

I am unable to design a code to render one particular post and it's related comments. The issue is maybe in views.py or the url.
I have looked at multiple sources without any results. I am a novice to coding and feel like I am missing some essential point. Posts and comments are getting created correctly and all comments get the correct post_id assigned.
My models.py is set up like this:
class Post(models.Model):
title = models.CharField(max_length=1000)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog-home')
class Comment(models.Model):
cid = models.AutoField(primary_key=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
comment = models.TextField()
comment_date = models.DateTimeField(default=timezone.now)
def save(self, *args, **kwargs):
super(Comment, self).save(*args, **kwargs)
def __str__(self):
return self.comment
def get_absolute_url(self):
return reverse('blog-home')
My views.py is set up like this:
class PostDetailView(DetailView):
model = Post
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comment_list'] = Comment.objects.filter(post=WHAT SHOULD GO HERE?)
return context
I need to pass the Post.id or primary key of the post in the filter above. Can someone explain what it should be?
The url used to access the post detail is as follows:
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail')
I get the post detail view rendered out as the author, title and content of the post when I have the following in views.py:
class PostDetailView(DetailView):
model = Post
The template for that is as below:
{% extends "blog/base.html" %}
{% block content%}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{object.author.profile.image.url}}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{% url 'user-posts' object.author.username %}">{{ object.author }}</a>
<small class="text-muted">{{ object.date_posted|date:"F d, Y P e" }}</small>
{% if object.author == user %}
<div><a class="btn btn-secondary btn-sm m-1 mb-1" href="{% url 'post-update' object.id%}">Update</a>
<a class="btn btn-danger btn-sm m-1 mb-1" href="{% url 'post-delete' object.id%}">Delete</a></div>
{% endif %}
</div>
<h2 class="article-title">{{ object.title }}</h2>
<p class="article-content">{{ object.content }}</p>
</div>
</article>
{% for comment in comment_list %}
<div class='article-content'>
<p>{{comment}}</p>
</div>
{% endfor %}
{% endblock %}
How should I take the post.id or pk of the Post and use it to filter the comments related only to that particular post?
Also, what is a good way to set up a template for rendering the queryset?
You should be able to iterate over the reverse link from Post object to the Comment objects linked to it (by default as comment_set) in your template:
{% for comment in post.comment_set %}
If you want greater control you inject a queryset into the context, something like this, to get the most recent six comments only.
"comments": post.comment_set.order_by("-comment_date")[:6]
The post object selected should be available as self.object in a DetailView and will be default be injected into the render context as object. An invaluable resource for navigating the structure of Django Class-based views is the Classy CBV
site.
Warning: this is "off the top of my head" so don't assume it's all perfect.
A single object will have access to its related objects.
Try this:
class PostDetailView(DetailView):
model = Post
# There's no need to define get_context_data() here
Now your template will have the post available as post (and also object).
All of the comments that have this post are available on the template like this:
{% for comment in post.comment_set.all %}
<div class='article-content'>
<p>{{ comment }}</p>
</div>
{% endfor %}

Django Foreign Key in View - get first image to show for each user in list

I'm trying to make my dashboard show a list of users in your area. This so far works but I can not get the user's fist image to show. The current error message I am getting is "'QuerySet' object has no attribute 'id'"
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)
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)
user = User.objects.filter(pk=pk)
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.objects.filter(user_id=user.id)
}
return render(request, self.template_name, args)
dashboard.html
<h2>People near you</h2>
{% for user in users %}
<a href="{% url 've:view_profile_with_pk' pk=user.pk %}">
<h4>{{ user.username }}</h4>
<p>{{ user.images }}</p>
{% if images %}
{% for img in images %}
<a href="{{ img.image.url }}" target="_blank">
<img src="{{ img.image.url }}" class="" style="max-width: 300px">
</a>
{% endfor %}
{% else %}
<p>No images</p>
{% endif %}
</a>
{% 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 %}
user = User.objects.filter(pk=pk) return queryset. When yoy try later Images.objects.filter(user_id=user.id) it raise error. You need to get first object in queryset with first() method:
user = User.objects.filter(pk=pk).first()
Or use get instead:
user = User.objects.get(pk=pk)
but second option will raise DoesNotExist error if user with provided id does not exist. To handle this error you can use get_object_or_404, which return page not found in case of wrong id:
from django.shortcuts import get_object_or_404
user = get_object_or_404(User, pk=1)

How To Fix "NoReverseMatch" in django blog comments?

I've tried looking at quite a few posts first, and none of them seem to have an answer to my solution (at least not an obvious one). I'm still very new to Django, so I'm still getting a hang of how everything relates (in terms of the models, views, forms, etc). The blog was originally a carbon-copy of the djangogirls blog from their tutorial, but I wanted to extend it by adding comments from another tutorial on the web. I ran into an error that I couldn't figure out afterwards.
Here's the source code for the comments:
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
exclude = ["post"]
views.py
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
comments = Comment.objects.filter(post=post)
d = dict(post=post, comments=comments, form=CommentForm(),
user=request.user)
d.update(csrf(request))
return render_to_response('blog/post_detail.html', d)
def add_comment(request, pk):
"""Add a comment to a post."""
p = request.POST
if p.has_key("body") and p["body"]:
author = "Anonymous"
if p["author"]: author = p["author"]
comment = Comment(post=Post.objects.get(pk=pk))
cf = CommentForm(p, instance=comment)
cf.fields["author"].required = False
comment = cf.save(commit=False)
comment.author = author
comment.save()
return redirect("dbe.blog.views.post_detail", args=[pk])
models.py
class Comment(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
author = models.CharField(max_length=60)
body = models.TextField()
post = models.ForeignKey(Post)
def __unicode__(self):
return unicode("%s: %s" % (self.post, self.body[:60]))
class CommentAdmin(admin.ModelAdmin):
display_fields = ["post", "author", "created_date"]
url pattern (urls.py):
url(r'^add_comment/(\d+)/$', views.post_detail, name='add_comment')
post_detail.html:
<!-- Comments -->
{% if comments %}
<p>Comments:</p>
{% endif %}
{% for comment in comments %}
<div class="comment">
<div class="time">{{ comment.created_date }} | {{ comment.author }}</div>
<div class="body">{{ comment.body|linebreaks }}</div>
</div>
{% endfor %}
<div id="addc">Add a comment</div>
<!-- Comment form -->
<form action="{% url add_comment post.id %}" method="POST">{% csrf_token %}
<div id="cform">
Name: {{ form.author }}
<p>{{ form.body|linebreaks }}</p>
</div>
<div id="submit"><input type="submit" value="Submit"></div>
</form>
{% endblock %}
The error is highlighting this line in post_detail.html:
<form action="{% url add_comment post.id %}" method="POST">{% csrf_token %}
And the error itself says:
NoReverseMatch at /post/1/
Reverse for '' with arguments '(1,)' and keyword arguments '{}' not found. 0 pattern(s) tried: []
And the traceback from django is insanely unwieldy, but it first points back to this line in views.py:
return render_to_response('blog/post_detail.html', d)
If you are using Django newer than 1.4, you need to quote your add_comment:
<form action="{% url 'add_comment' post.id %}" method="POST">

Categories