Django How to Post comment on only one List item - html

I am trying to create django commerce app I am little bit stuck on a thing
When I post comment via form I created
<form action="{% url 'comment' list_id.id %}" method="POST">
{% csrf_token %}
<textarea name="comment" class="inp-cmt" rows="3"></textarea>
<input type="submit">
</form>
the comment is posted but it post on all of my list page I wanted only on the page where comment is posted
my comment section
{% if allcomments %}
<h1>Comments</h1>
<div class="card-cmt">
{%for com in allcomments%}
<li style="list-style: none;">
<footer class="post-info">
<span>{{com.user}}</span>
<p>{{com.text}}</p>
</footer>
</li>
{% endfor %}
</div>
{% endif %}
my urls
urlpatterns = [
path("", views.index, name="index"),
path("login", views.login_view, name="login"),
path("logout", views.logout_view, name="logout"),
path("register", views.register, name="register"),
path("newlist", views.create_listing, name="new_list"),
path("item", views.add_item, name="new_item"),
path("listing/<int:list_id>", views.listing, name="listing"),
path("delete/<int:item_id>", views.delete_list, name="delete"),
path("comment/<int:list_id>", views.comments, name="comment")
]
my views for comment and listing
def comments(request, list_id):
coms = Comments()
if request.method == 'POST':
coms.user = request.user.username
coms.text = request.POST.get('comment')
coms.listid = list_id
coms.save()
return redirect('listing', list_id)
else :
return redirect('index')
def listing(request, list_id):
list_item = Listing.objects.get(id=list_id)
return render(request, "auctions/listing.html",{
"list_id" : list_item,
"allcomments" : Comments.objects.all()
})
models
class Listing(models.Model):
owner = models.CharField(max_length =64,default="N/A")
productname = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
description = models.CharField(max_length=999, default="test")
date = models.DateField(auto_now_add=True)
link = models.CharField(max_length=200, default="test1")
def __str__(self):
return f"{self.owner} {self.productname} {self.price} {self.date} {self.description} {self.link}"
class Comments(models.Model):
user = models.CharField(max_length=64)
text = models.TextField()
date = models.DateTimeField(auto_now_add=True)
listid = models.IntegerField(default=0)
def __str__(self):
return f"{self.user} {self.text} {self.date} {self.listid}"

You're returning all comments on every listing when you do "allcomments" : Comments.objects.all()
The problem is in your listing function. Try this instead:
def listing(request, list_id):
list_item = Listing.objects.get(id=list_id)
return render(request, "auctions/listing.html",{
"list_id" : list_item,
"allcomments" : Comments.objects.filter(listid=list_id)
})
Notice the change - from "allcomments" : Comments.objects.all() to "allcomments" : Comments.objects.filter(listid=list_id)
Also, your implementation for class Comments and class Listing could be a bit better. Have you ever come across something called a ForeignKey? It will be a lot more efficient to use that. https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.ForeignKey

Related

How do I use a for loop to reuse a django form in a template

After struggling with this issue for a while, I am hoping someone here can point me in a more productive direction.
I am trying to take an indeterminate number of variables in a database (obtained through a different template) and render them on a webpage, each variable with a simple data entry form, to be saved back to the database. Basically, it's a tracker for analysis. Say I want to track my daily sleep, running time, and calorie intake (the variables). I have those saved in a database as variables and want to call upon those variables and show them on a webpage with a daily entry form. I am using a "for" loop right now and it renders the way I want it to, with the variable name and the form, but it is only saving the last item in the variable list. How do I amend the code below such that when I hit the save button for each form rendeded, it saves the information for that variable (not just the last one rendered).
Below is the code. Any and all help would be infinitely appreciated.
Models...
class Variable(models.Model):
date_added = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(get_user_model(), default='', on_delete=models.CASCADE) # id the active user
ENTRY_TYPE_CHOICES = [
('numeric', 'enter a daily number'),
('scale', 'rate daily on a scale of 1-10'),
('binary', "enter daily, 'yes' or 'no' "),
]
variable = models.CharField(max_length=50, default='')
entry_type = models.CharField(max_length=50, choices=ENTRY_TYPE_CHOICES, default="numeric")
def __str__(self):
return self.variable
class DailyEntry(models.Model):
date = models.DateField(default=datetime.date.today)
# date_added = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(get_user_model(), default='', on_delete=models.CASCADE) # id the active user
variable_name = models.CharField(max_length=50, default='')
variable_id = models.SmallIntegerField(default=0000)
entry = models.FloatField(default=9999)
class Meta:
verbose_name_plural = 'Daily Entries'
def __str__(self):
return self.variable
Form...
class VariablesForm(forms.ModelForm):
class Meta:
model = Variable
fields = ['variable', 'entry_type' ]
labels = {'variable':'Dependent variable to track', 'entry_type': 'Type of measure'}
class DailyEntryForm(forms.ModelForm):
class Meta:
model = DailyEntry
fields = ['variable_name', 'variable_id', 'entry', 'date']
labels = {'entry': 'Daily entry', 'date': 'Date'}
widgets = {'variable_name': forms.HiddenInput(), 'variable_id': forms.HiddenInput()}
Views...
def daily_entry(request):
''' page to make daily entries '''
vars = Variable.objects.filter(id__gt = 0 )
if request.method != 'POST':
# No data submitted. GET submitted. Create a blank form
form = DailyEntryForm()
else:
#POST data submitted. Process data
form = DailyEntryForm(data=request.POST)
if form.is_valid():
data = form.save(commit=False)
data.created_by = request.user
data.save()
return HttpResponseRedirect(reverse('entry_new'))
context = {'form': form, 'vars': vars}
return render(request, 'entry_new.html', context)
and HTML...
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
{% for var in vars %}
<div>
<ul>
<h3>{{ var.variable }}</h3>
<form class="" action="" method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="hidden" name="variable_id" value="{{ var.id }}" >
<input type="hidden" name="variable_name" value="{{ var.variable }}">
<input type="submit" name="" value="Save" />
</ul>
</div>
{% endfor %}
{% endblock content %}
Any help, well, helps...
Thanks!

I can submit data but won't go to new page -or- go to new page and data doesn't get saved in Django

So the title kind of says it all. I have a form based on a model. The goal is to have the specific data entered, user presses submit, data gets saved to a Postgresql DB, user sees complete.html. However this is not the case. I can either get the data to save without changing pages, or the page will change but the data will not submit. I have put details on the specifics of I tried below.
urls.py
from django.urls import path, include
from . import views
urlpatterns = [
path('', views.index, name='index-page'),
path('feedback/', views.feedback_view, name = 'feedback'),
path('complete/', views.complete, name = 'complete')
]
models.py
from django.db import models
from django.utils import timezone
from django.core.validators import RegexValidator
class feedback(models.Model):
RECENT_GROUND = [
('PVT','Private'),
('INS','Instrument'),
('COM','Commercial'),
('MEL','Multi-Engine'),
]
RATING = [
(1,'Poor'),
(2,'Below Average'),
(3,'Average'),
(4,'Above Average'),
(5,'Excellent'),
]
id_field = models.AutoField(primary_key = True, serialize = True)
added = models.DateTimeField(default = timezone.now)
spid = models.CharField(primary_key = False, max_length=5, verbose_name = 'Enter the students FSA ID number:')
recent_ground = models.CharField(max_length = 3, choices = RECENT_GROUND, verbose_name = 'Most recently completed ground school:')
question1 = models.IntegerField(choices = RATING, verbose_name = 'This is question 1')
question2 = models.IntegerField(choices = RATING, verbose_name = 'This is question 2')
question3 = models.IntegerField(choices = RATING, verbose_name = 'This is question 3')
question4 = models.IntegerField(choices = RATING, verbose_name = 'This is question 4')
question5 = models.IntegerField(choices = RATING, verbose_name = 'This is question 5')
question6 = models.IntegerField(choices = RATING, verbose_name = 'This is question 6')
question7 = models.IntegerField(choices = RATING, verbose_name = 'This is question 7')
comments = models.TextField(max_length = 500, verbose_name = 'Please leave any additional comments below. If you do not wish to leave comments, type "none".')
def __str__(self):
return self.title
forms.py
#imports forms class from django
from django import forms
#imports the feedback class from ipfeedbackdb/models.py
from ipfeedbackdb.models import feedback
class feedbackForm(forms.ModelForm):
class Meta:
model = feedback
fields = "__all__"
exclude = ['recID','added']
views.py
from django.shortcuts import render
from .forms import feedbackForm as feedform
def index(request):
return render(request, 'index/index.html', {'title':' Home'})
# def feedback(request):
# return render(request, 'index/feedback.html', {'title':' Collection'})
def feedback_view(request):
context = {}
form = feedform(request.POST or None, request.FILES or None)
if form.is_valid():
form.save()
context['form'] = form
return render(request, "index/feedback.html", context)
def complete(request):
return render(request, 'index/complete.html', {'title':' Submitted'})
base.html (removed most of the styling to reduce length)
{% load static %}
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="{% static 'index/override.css' %}">
{% if title %}
<title>Feedback{{ title }}</title>
{% else %}
<title>Feedback Site</title>
{% endif %}
<script type="text/javascript">
window.history.forward();
function noBack() { window.history.forward(); }
</script>
</head>
<body onload="noBack();" onpageshow="if (event.persisted) noBack();" onunload="">
<div class = 'jumbotron jumbotron-fluid'>
<div class = 'container'>
<h2 class = 'display-4' style = 'color:#fff;margin-left: -100px'>FSA</h2>
<p class = 'lead' style = 'color:#fff;;margin-left: -97px'>Feedback</p>
</div>
</div>
<div>
{% block content %}
{% endblock content %}
</div>
</body>
</html>
index.html
{% extends 'index/base.html' %}
{% block content %}
<div>
<h2>Index Page</h2>
<p>This is some more added text.</p>
</div>
<div>
Next ->
</div>
{% endblock content %}
feedback.html (action = "" saves data without changing pages or clearing form, action ="/feedback/" will save but not clear the form or go to another page. action = "/complete/" took me to complete.html but it did not submit the data to the database.)
{% extends 'index/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div>
<h2>Feedback Form Page</h1>
</div>
<div>
<form action = "" method = "post">
{% csrf_token %}
{{ form| crispy }}
<input type="submit" name="Submit">
</form>
</div>
{% endblock content %}
complete.html
{% extends 'index/base.html' %}
{% block content %}
<h2>Your feedback has been submitted. Thank you!</h2>
<div>
Return to start
</div>
{% endblock content %}
In your feedback view, once forms is valid it doesn't go to complete.
def feedback_view(request):
context = {}
form = feedform(request.POST or None, request.FILES or None)
if form.is_valid():
form.save()
return render(request, "index/complete.html", context)
else:
context['form'] = form
return render(request, "index/feedback.html", context)
use button tag!
replace:
<button type="submit">submit</button>
with:
<input type="submit" name="Submit">
change your view like below
views.py:
from django.contrib import messages
from django.shortcuts import redirect
def feedback_view(request):
context = {}
form = feedform(request.POST or None, request.FILES or None)
if request.method == 'POST':
if form.is_valid():
form.save()
return redirect('app_name:url_name') # change to where you want to redirect after a successful from submition
messages.success(request, "Form submitted successfully")
else:
messages.warning(request, form.errors)
context['form'] = form
return render(request, "index/feedback.html", context)

Where can I add form control in my html when loading a registration form and iterating over that form?

So I'm loading my registration form into my html and then using a for loop to iterate over those fields. I am wondering where I can add form control to show a green box around my username field so that a user knows if a username is taken before hitting the submit button. I tried adding it to the form tag and setting a div tag around {{field}} but neither of those work. Furthermore, how can I make it ONLY for Username?
registration.html
{% block content %}
<br>
<h1 class="text-center" style="color:#f5387ae6">Register to fall in love today!</h1>
<form method="post" style="width:700px;margin:auto" action="{% url 'dating_app:register' %}" enctype="multipart/form-data" class= "form" >
{% bootstrap_form registration_form%}
{% csrf_token %}
{% for field in bootstrap_form %}
<p>
<div class="form-control is-valid">
{{field.label_tag}}
{{field}}
</div>
{% if field.help_text %}
<small style="color:grey;">{{field.help_text}}</small>
{% endif %}
{% for error in field.errors %}
<p style="color: red;">{{error}}"</p>
{% endfor %}
</p>
{% endfor %}
<div class="form-check">
<input type="checkbox" id="accept-terms" class="form-check-input">
<label for="accept-terms" class="form-check-label">Accept Terms & Conditions</label>
</div>
<div>
<br>
<button type="submit">Register</button>
</div>
</form>
{% endblock content %}
reg_form
class RegistrationForm(UserCreationForm):
class Meta:
model = Profile
fields = ("username","email","description","photo","password1","password2")
models.py
class ProfileManager(BaseUserManager):
def create_user(self, username, email,description,photo, password=None):
if not email:
raise ValueError("You must creat an email")
if not username:
raise ValueError("You must create a username!")
if not description:
raise ValueError("You must write a description")
if not photo:
raise ValueError("You must upload a photo")
user = self.model(
email=self.normalize_email(email),
username = username,
description= description,
photo= photo,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, email,description,photo, password):
user = self.create_user(
email=self.normalize_email(email),
password=password,
username=username,
description=description,
photo=photo,
)
user.is_admin=True
user.is_staff=True
user.is_superuser=True
user.save(using=self._db)
return user
class Profile(AbstractBaseUser):
class Meta:
swappable = 'AUTH_USER_MODEL'
email = models.EmailField(verbose_name="email")
username = models.CharField(max_length=30, unique=True)
date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
#what I added
description = models.TextField()
photo = models.ImageField(upload_to='profile_photo',blank=False, height_field=None, width_field=None, max_length=100)
matches = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='+', blank=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['description','photo','email']
objects = ProfileManager()
def __str__(self):
return self.username
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self,app_label):
return True
class Conversation(models.Model):
members = models.ManyToManyField(settings.AUTH_USER_MODEL)
class UserVote(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
voter = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='given_vote', on_delete=models.CASCADE)
vote = models.BooleanField(default=False)
class Meta:
unique_together = (('user', 'voter'))
class InstantMessage(models.Model):
sender = models.ForeignKey(settings.AUTH_USER_MODEL, related_name= 'sender',on_delete=models.CASCADE )
receiver = models.ForeignKey(settings.AUTH_USER_MODEL, related_name= 'receiver',on_delete=models.CASCADE )
conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE)
message = models.TextField()
date = models.DateTimeField(verbose_name="Data creation",default=timezone.now, null=False)
viewed = models.BooleanField(default=False, db_index=True)
def __unicode__(self):
return self.message
#tests to see if messages are exclusive between sender, receiver (won't work with new model)
#classmethod
def find_messages_exclusive_to_profile(cls,sender,receiver):
#members = receiver AND sender, not receiver or sender
exclusive_conversations = Conversation.objects.filter(members= receiver ).filter(members= sender)
exclusive_messages = InstantMessage.objects.filter(conversation__in=exclusive_conversations)
return exclusive_messages
You will need to setup a view which, given a string can check to see if that object exists in the database:
# views.py
from django.contrib.auth.models import User
from django.http import JsonResponse
def check_if_username_exists_view(request, username):
name_taken = User.objects.filter(username=username).exists()
data = {
'name_taken ': name_taken
}
return JsonResponse(data)
Then within your login page you will need some javascript similar to the below:
$("#username").change(function(val){
$.get( {% url 'user_exists_view' %} + val, function( data ) {
alert( "username taken: " data.name_taken );
});
})

Django - Filter Model Objects (Posts) Using Form

So I have a model called Blog
class Blog(models.Model):
tagoptions = (
(u'Project', u'Project'),
(u'IT', u'IT'),
(u'Robotics', u'Robotics'),
. . .
(u'-', u'-'),
)
user = models.TextField(blank=True, null=True)
title = models.CharField(max_length=50, default='')
context = models.TextField(max_length=5000, default='')
ireceived = models.IntegerField(default=0, blank=True, null=True)
personnelneeded = models.IntegerField(default=1)
datewritten = models.DateField(default=datetime.date.today, blank=True, null=True)
tags = models.CharField(max_length=100, choices=tagoptions, default='-')
def __str__(self):
return str(self.user) + ": id" + str(self.id) + " - title: " + str(self.title)
and a form for filtering the objects using "tags"
class FilterOption(forms.Form):
tagoptions = (
(u'showall', u'Show All'),
(u'Project', u'Project'),
(u'IT', u'IT'),
. . .
(u'-', u'-'),
)
tags = forms.ChoiceField(choices=tagoptions, initial="showall")
tags.widget.attrs.update({'style' : 'color: black;'})
This is the part of the form for filtering in my template.
<form method="post" action="{% url 'posts' %}">
{% csrf_token %}
<p>{{ form.tags }} <input type="submit" name="filter" value="Filter"/></p>
</form>
Here is what I use in my template to display the top 10 most recent posts.
{% for p in postshow|slice:":10" %}
<br><br>
<div class="postdisplay">
<h1><a class="stuffurl" href="{% url 'posted' postid=p.id %}"><u>{{ p.title }}</u></a></h1>
<h4>Post by <a class="stuffurl" href="{% url 'user' user_login_name=p.user %}"><u>{{ p.user }}</u></a>, {{ p.datewritten}}</h4>
<h4>Tag: {{ p.tags }}
</div>
<br><br>
{% endfor %}
Then finally in my views I have
def posts(request):
postshow = Blog.objects.order_by('-id')
if request.method == 'POST':
form = FilterOption()
if form.is_valid():
filteropt = form.cleaned_data['tags']
if filteropt != showall:
postshow = Blog.objects.order_by('-id')
else:
postshow = Blog.objects.filter(tags=filteropt).order_by('-id')
else:
form = FilterOption()
context = {'postshow' : postshow, 'form' : form}
return render(request, 'home/posts.html', context)
When ever I submit the form, it's supposed to check if the input says show all or not. If it does then it does not filter anything and if not it filters the objects so that only the objects with the tag of the input gets shown. However, the code seems to be stuck at form.is_valid (I checked using print). Another possible problem is that even if it worked pass form.is_valid, the filter won't work since the input is refreshed whenever you submit the form.
It will be greatly appreciated if someone can help me fix my code so that I can select a tag and the post will be filtered and if I select "show all", it shows all posts without filtering them.
Before checking if the form is valid, try passing the request.POST data into it:
form = FilterOption(request.POST)

Django how to adding comments option on a post

I am developing a blog which i want to add comment form option to it, i have added the form to the same page directly under the article, i want that went a user comment it should redirect to the same page with the article but i keep getting and error
here is my code
view
def comment(request, article_id):
try:
article = Article.objects.get(pk=article_id)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.cleaned_data['comment']
article.comments_set.create(comment=comment)
#messages.infos(request,comment)
return redirect('blog:_article')
#else:
#pass
#form = CommentForm()
#context['form'] = form
#return render(request,'blog/comment.html', context)
except Exception as e:
#wriet error to file
return render(request,'blog/404.html')
urls
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.index, name='index'),
path('<int:article_id>/article', views._article, name='_article'),
path('<int:article_id>/comment', views.comment, name='comment'),
]
models
class Comments(models.Model):
comment = models.TextField()
date = models.DateTimeField(default=timezone.now)
article = models.ForeignKey(Article, on_delete=models.CASCADE)
def __str__(self):
return self.comment
form
<form method="post" action="{% url 'blog:comment' article.id %}">
{% csrf_token %}
{% for field in form %}
{{ field.label_tag }}
{% render_field field class="form-control is-valid" rows="4" %}
{% endfor %}<br>
<button class="btn btn-success">Post</button>
</form>
I finally did it by adding the code to handle the comment in the same view that renders the articles this is my code
def _article(request, article_id):
try:
article = Article.objects.get(pk=article_id)
related_articles = Article.objects.filter(tags=article.tags).exclude(pk=article.pk)[:4]
context['article'] = article
context['related_articles'] = related_articles
context['form'] = CommentForm()
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.cleaned_data['comment']
article.comments_set.create(comment=comment)
return render(request,'blog/article.html', context)
except Exception as e:
#write error to file
return render(request,'blog/404.html')
If you don't want the page to redirect to another page or update the page, you should use AJAX (https://www.w3schools.com/jquery/jquery_ajax_get_post.asp) in this case. Your form will hit the url in the action by changing your page to that url so you have to handle the redirecting and rendering in your commenting view to come back to same page if you don't wanna do this dynamically.