Trying to implement poll-creating system, where I can create questions with choices (previously I did this all in the admin, and had to create an individual choice object for every choice)
Here's my models:
class Question(models.Model):
has_answered = models.ManyToManyField(User, through="Vote")
question_text = models.CharField(max_length=80)
date = models.DateTimeField(auto_now=True)
def __str__(self):
return self.question_text
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=100)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text
So right now here's the template:
<form method="post" action="">{% csrf_token %}
{{ question_form.question_text }}
<br><br>
<!--{{ choice_form.choice_text|placeholder:"Choice" }}-->
<input class="choice" name="choice_text" placeholder="Choice" type="text" />
<input class="choice" name="choice_text" placeholder="Choice" type="text" />
<input class="choice" name="choice_text" placeholder="Choice" type="text" />
<img src="{% static 'images/plus.png' %}" class="add_choice" />
<br>
<button class="submit" type="submit">Create question</button>
</form>
As you can see I'm not sure whether using multiple {{ choice_form.choice_text|placeholder:"Choice" }} is possible. So I paste multiple input fields and tried to get all of them by using getList(). However I get this error:
'QueryDict' object has no attribute 'getList'
Any idea why? Here's my views:
def questions(request):
question_form = QuestionForm(request.POST or None)
choice_form = ChoiceForm(request.POST or None)
if request.POST:
choice = request.POST.getList('choice_text')
print(choice)
if request.user.is_authenticated():
if question_form.is_valid():
print('valid question')
#question = question_form.save(commit=False)
return render(request, 'questions.html', {'question_form': question_form, 'choice_form': choice_form})
So how exactly should I go about this, for grabbing every choice input? Eventually I want to make all these choices map to the question that was entered. But as I said I'm sure sure whether it's possible to have multiple instances of the same field in one form.
Try using getlist instead of getList.
Also consider using forms, specifically the MultipleChoiceField could be useful in your scenario.
Related
I am adding a like functionality in my website where users can like each others posts.
I have done this successfully, however have one issue. This is checking whether the user has already liked the post, which has to be performed specifically in my HOME view.
This is so I can render my home page. To encounter this issue, I perform a .annotate() on my posts when retrieving them, and see if a user has liked a post.
I then pass this onto my home template and check if the user exists within the posts likes property.
Here's the related code.
models.py:
class Post(models.Model):
file = models.ImageField(upload_to='images/')
summary = models.TextField(max_length=600)
pub_date = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
likes = models.ManyToManyField(User, through='Like', related_name='likes')
def __str__(self):
return self.user.username
def pub_date_pretty(self):
return self.pub_date.strftime('%b %e %Y')
def summary_pretty(self):
return self.summary[:50]
#property
def total_likes(self):
return self.likes.count()
class Like(models.Model):
status = models.BooleanField()
post = models.ForeignKey(Post, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
views.py:
def home(request):
posts = Post.objects.all()
liked_or_not = Post.objects.annotate(likes__user=request.user)
return render(request, 'posts/home.html', {'posts': posts, 'liked_or_not': liked_or_not})
home.html:
{% if liked_or_not == True %}
<button class="btn btn-primary btn-lg btn-block"><span class="oi oi-caret-top"></span> Unlike {{ post.total_likes }} </button>
{% else %}
<button class="btn btn-primary btn-lg btn-block"><span class="oi oi-caret-top"></span> Like {{ post.total_likes }} </button>
{% endif %}
<form id="likepost{{ post.id }}" method="POST" action="{% url 'likepost' post.id %}">
{% csrf_token%}
<input type="hidden">
</form>
For those coming here for other possibilities in debugging the error message, I had made a mistake and entered in a query analogous to
Like.objects.filter(user=request.user).values('status', flat=True)
# instead of the correct
Like.objects.filter(user=request.user).values_list('status', flat=True)
I don't quite understand what do you want to do. If you want to check if the user liked at least one post, you can do it like this:
liked_or_not = Like.objects.filter(user=request.user).exists
If you want to check if a user liked a specific post, you can dot it like this:
liked_or_not = Likes.objects.filter(post_id=post_id, user=request.user).exists()
annotate has a different purpose. It annotates each object in the QuerySet with the provided list of query expressions. An expression may be a simple value, a reference to a field on the model (or any related models), or an aggregate expression (averages, sums, etc.) that has been computed over the objects that are related to the objects in the QuerySet. read more here https://docs.djangoproject.com/en/2.2/ref/models/querysets/#annotate
I encounterd similar problem and solved using .annotate
and below is my definition of Like in models.py
class Like(models.Model):
user = models.ForeignKey(
"users.User", on_delete=models.CASCADE, related_name="likes"
)
writing = models.ForeignKey(
"writings.Writing", on_delete=models.CASCADE, related_name="likes"
)
in views.py
filter_kwargs = {}
filter_kwargs["writing"] = OuterRef("pk")
filter_kwargs["user"] = request_user
subquery = Like.objects.filter(**filter_kwargs)
writing = (
Writing.objects.annotate(user_likes=Exists(subquery))
.get(id=id)
)
I was trying to add a comment function to my django app, but whatever I did, the comment submitted just wouldn't show up in database, can't figure out why. Here're some codes I worked with, please have a look.
the models.py part
class Comment(models.Model):
name = models.CharField(max_length=80)
email = models.EmailField()
text = models.TextField()
post = models.ForeignKey(Post, related_name='comments')
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ('created',)
verbose_name="评论"
verbose_name_plural="评论"
def __str__(self):
return 'Comment by {} on {}'.format(self.name, self.post)
the forms.py part
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model=Comment
fields=('name','email','text')
the views.py part
def post_comment(request,id):
post=get_object_or_404(Post,id=id)
if request.method=='POST':
form=CommentForm(request.POST)
if form.is_valid():
name=request.POST.get('name','')
email=request.POST.get('email','')
text=request.POST.get('text','')
comment=Comment(name=name,email=email,text=text,post=14)
comment.save()
return redirect(post)
else:
form=CommentForm()
comment_list = post.comments.all()
context={
'post':post,
'form':form,
'comment_list':comment_list
}
return render(request,'post_detail.html',context=context)
return redirect(post)
and the post_detail.html part
<form action="" method="POST" class="mb-5">{% csrf_token %}
<div class="form-group">
<label for="exampleInputPassword1">名称</label>
<input name="name" type="name" class="form-control" id="exampleInputPassword1" placeholder="Name">
</div>
<div class="form-group">
<label for="exampleInputEmail1">邮箱</label>
<input name="email" type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email">
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div class="form-group">
<label for="exampleFormControlTextarea1">评论</label>
<textarea name="text" class="form-control" id="exampleFormControlTextarea1" placeholder="Comment area..." rows="5"></textarea>
</div>
<button type="submit" class="btn btn-primary" value="post_comment">提交</button>
</form>
Any suggestions? Thanks.
You are doing two things wrong here. The first is that you are rendering the form manually and you do not have anything in your HTML to display form errors. You need to add the errors to your HTML as described in the documentation - or until yo get this sorted out use {{ form.as_p }}
Even after that, it still wont work because your python code in your view is wrong.
if form.is_valid():
name=request.POST.get('name','')
email=request.POST.get('email','')
text=request.POST.get('text','')
comment=Comment(name=name,email=email,text=text,post=14)
comment.save()
return redirect(post)
else:
form=CommentForm()
The last two lines of code above results in a new form being created when teh form is invalid. That discards any form data that was posted. Which also means any errors you had will be discarded along the way. The correct code will look something like the following:
post=get_object_or_404(Post,id=id)
if request.method=='POST':
form=CommentForm(request.POST)
if form.is_valid():
# don't directly use the request post data. Always use form.cleaned_data
name= form.cleaned_data['name'
.....
comment=Comment(name=name,email=email,text=text,post=14)
comment.save()
# the following redirect is wrong. Here post is an object
# you retrieved from a database, we usually redirect to a URL
return redirect(post)
else:
form = CommentForm()
comment_list = post.comments.all()
context={
'post':post,
'form':form,
'comment_list':comment_list
}
return render(request,'post_detail.html',context=context)
try this to creating comment class:
class Comment(models.Model):
post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, related_name='comments')
author = models.CharField(max_length=200)
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
Register Comment model in the admin panel
To register the Comment model in the admin panel, go to blog/admin.py and add this line:
admin.site.register(Comment)
directly under this line:
admin.site.register(Post)
Remember to import the Comment model at the top of the file, too, like this:
from django.contrib import admin
from .models import Post, Comment
admin.site.register(Post)
admin.site.register(Comment)
Make our comments visible HTML:
<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 %}
I am trying to figure out how to access fields from a Model that is used as a ForeignKey within the Model that the forms are querying.
Each Room has a form where the user selects a Layout from a dynamic list of possible Layout objects.
1—The HTML forms/room/update/layouts.html
<form class="layouts__form form-horizontal" action="" method="post" enctype="multipart/form-data">
<fieldset class="form__options">
{% csrf_token %}
{% for field in form.layout %}
<div class="layouts__layout">
{{ field.tag }}
{{ field.choice_label }}
<label for="value_{{ forloop.counter0 }}">
<div class="layouts__layout__thumbnail layouts__layout__thumbnail--{{ field.choice_label }}" style="background-image: url('### (I WOULD LIKE TO LOAD 'Layout.thumbnail' HERE) ###');"></div>
</label>
</div>
{% endfor %}
<div class="form__submit">
<button type="submit">Submit</button>
</div>
</fieldset>
</form>
2—The form is being called by this in views.py:
class RoomLayoutView(UpdateView):
model = Room
form_class = RoomLayoutForm
template_name = 'forms/room/update/layouts.html'
3—Which is being created by this in forms.py:
class RoomLayoutForm(forms.ModelForm):
layout = forms.ModelChoiceField(
widget=forms.RadioSelect(attrs={'type': 'radio', 'id': 'value',}),
queryset=Layout.objects.all(),
required=False, empty_label=None)
class Meta:
model = Room
fields = ['layout']
4—Which uses the Room model from:
class Room(models.Model):
title = models.CharField(max_length=200, blank=True)
layout = models.ForeignKey(Layout, related_name='template_selected', blank=True, null=True)
def __str__(self):
return self.title
5—Which takes one of the Layout models as a ForeignKey defined here:
class Layout(models.Model):
title = models.CharField(max_length=200)
...
padding_top = models.IntegerField(blank=False, default=0)
...
thumbnail = models.FileField(upload_to='layouts')
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-title',)
def __str__(self):
return self.title
I am trying to figure out how to access attributes from Layout model within the actual form. I would especially like to dynamically load the Layout.thumbnail or Layout.padding_top within the form at the top. I have tried at least 8 different methods and was unable to figure out a way to make this work. Any help would be really appreciated.
As stated in this answer, you can access the current instance associated with the form like this in your template:
{{ form.instance }}
So to access the thumbnail or padding_top attributes of the linked layout:
{{ form.instance.layout.thumbnail }}
{{ form.instance.layout.padding_top }}
I used this code previously it worked fine and i was suggested to use ModelForm by another member, it did make sense to use the form.is_valid() function etc.. so thought of giving it a try.
I went through some other examples on the internet but mine does not seem to work for some reason, or may be I am not doing it right, I get the following when I print the form in the view, and it goes to the else statement, so my form does not get saved
<input id="id_product" type="text" name="product" value="aassddf" maxlength="250" />
FAIL
My model.py
from django.db import models
from django.forms import ModelForm
class Category(models.Model):
name = models.CharField(max_length=250)
def __unicode__(self):
return self.name
class Product(models.Model):
category = models.ForeignKey(Category)
product = models.CharField(max_length=250)
quantity = models.IntegerField(default=0)
price = models.FloatField(default=0.0)
def __unicode__(self):
return self.product
class ProductForm(ModelForm):
class Meta:
model = Product
My views.py
from models import *
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
def index(request):
...
...
def add_product(request):
if request.method == 'POST':
form = ProductForm(request.POST)
print form['product']
if form.is_valid():
form.save()
return HttpResponseRedirect('/product')
else:
print 'FAIL'
return HttpResponseRedirect('/product')
My html
<form method="post" action="add_product/">
{% csrf_token %}
<label for="category">Category</label>
<select name="category" id="category">
{% for category in category_list %}
<option> {{ category.name }} </option>
{% endfor %}
</select>
<label for="product">Product</label>
<input type="text" name="product" id="product">
<label for="quantity">Quantitiy</label>
<input type="text" name="quantity" id="quantity">
<label for="price">Price</label>
<input type="text" name="price" id="price">
<input type="submit" value="Add New product" id="create">
</form>
Is there a better way i could save the data, using ModelForms ??
Thanks in advance for the help.
You should read the documentation. If the form is not valid, it will have a whole set of errors associated with it, which will tell you exactly why. But you just throw that away, and redirect to /product. The docs show exactly how to redisplay the form with the errors.
Also you should not write HTML form field tags directly in your template: use the form object from the view - {{ form.product }}, etc - as these will be repopulated with the appropriate values on redisplay.
Thanks to Daniel Roseman and Anuj Gupta I think I finally re-worked on my code on got it working in a standard way so it will generate the html form and validate errors.
So for anyone else who is trying to work django forms here is the code I worked on.
My model.py is was almost the same one i posted on the question but i removed
class ProductForm(ModelForm):
class Meta:
model = Product
I created a new form.py here is the code-
from django import forms
from models import Category
class ProductForm(forms.Form):
# Put all my Categories into a select option
category = forms.ModelChoiceField(queryset=Category.objects.all())
product = forms.CharField()
quantity = forms.IntegerField()
price = forms.FloatField()
My views.py changed had a lot of changes -
def add_product(request):
success = False
if request.method == "POST":
product_form = ProductForm(request.POST)
if product_form.is_valid():
success = True
category = Category.objects.get(name=product_form.cleaned_data['category'])
product = product_form.cleaned_data['product']
quantity = product_form.cleaned_data['quantity']
price = product_form.cleaned_data['price']
new_product = Product(category = category, product = product, quantity = quantity, price = price )
new_product.save()
new_product_form = ProductForm()
ctx2 = {'success':success, 'product_form':new_product_form}
return render_to_response('product/add_product.html', ctx2 , context_instance=RequestContext(request))
else:
product_form = ProductForm()
ctx = {'product_form':product_form}
return render_to_response('product/add_product.html', ctx , context_instance=RequestContext(request))
Finally in my html page i used {{ product_form.as_p }} so it created the forms dynamically
{% if success %}
<h3> product added successfully </h3>
{% endif %}
<form method="post" action=".">
{% csrf_token %}
{{ product_form.as_p }}
<input type="submit" value="Add New product" id="create">
<input type="reset" value="reset" id="reset">
</form>
This may not be the perfect solution, but for a starter like me this sounds good, and at times you just get lost while reading the docs lol, hope it helps some one.
Cheers
Try:
<form method="post" action="add_product/">
{% csrf_token %}
{{ form.as_p }}
</form>
in your template, instead of hand-coding the form's input tags. This shortcut will generate the form html for you, as well as print validation errors.
Make sure you return the form object to the template when:
There is no request.POST (form has not been submitted)
form.is_valid() fails (form has validation errors)
Of course, this is only to get you started. You really should read the docs
I am new to python and I am using django for student database application .
Student database application must show id, firstname,lastname,subjectnames,marks.
Single student is having multiple subjects and their marks.
I am getting problem with accessing multiple values that student is having multiple subjects and marks.
models.py
class Person(models.Model):
firstname=models.CharField(max_length=50)
lastname=models.CharField(max_length=50)
def __unicode__(self):
return (self.firstname,self.lastname)
class Marksheet(models.Model):
subname=models.CharField(max_length=50)
marks=models.IntegerField(max_length=10)
person=models.ForeignKey(Person)
def __unicode__(self):
return self.subname
views.py
def add_page(request,page_name): # function for creating the new records
p1=None
p2=None
if request.method=='POST':
p1=Person(firstname=request.POST['firstname'],lastname=request.POST['lastname'])
p1.save()
p2=Marksheet(subname=request.POST.getlist('subnames'),person=Person(person_id))
p2.save()
return render_to_response("add.html",{"page_name":page_name})
creating a records I am using form in html which is shown below....
Templates
add.html
<form method="post" action="/newdcl/{{page_name}}/add/" > {% csrf_token %}
First name: <input type="text" name="firstname" /> <br />
Last name: <input type="text" name="lastname" /> <br />
Operating System <input value="os" name="subnames" type="checkbox"><br />
System Programming <input value="sp" name="subnames" type="checkbox"> <br />
Maths <input value="maths" name="subnames" type="checkbox"> <br />
<input type="submit" value="save" >
</form>
Can anyone help me in this????
Your problem seems to lie in your how you try to create Marksheet, you can't assign a list of values to one field like that.
Using your currently formless, scary, no-validation, setup... you can do something like this-
p1=Person(firstname=request.POST['firstname'],
lastname=request.POST['lastname'])
p1.save()
for subname in request.POST.getlist('subnames'):
new = MarkSheet(subname=subname, person=p1)
#no data for marks, must define it to be able to be blank/null
new.save()
You will need to add blank=True, null=True to you marks field in your models.py if you intend to not have any initial mark.
Please look at Making Queries and Forms
In my opinion it should be done by using many-to-many relation in Person, and form should be defined as a form class, because you don't have any validation in your form, and you are writing some html, which could be generated by putting one line of code in template. I would do it like this:
models.py
class Marksheet(models.Model):
subname=models.CharField(max_length=50)
marks=models.IntegerField(max_length=10)
def __unicode__(self):
return self.subname
class Person(models.Model):
firstname=models.CharField(max_length=50)
lastname=models.CharField(max_length=50)
marksheets = models.ManyToManyField(Marksheet)
def __unicode__(self):
return (self.firstname,self.lastname)
forms.py
from models import Person
class PersonForm(ModelForm):
def __init__(self, *args, **kwargs):
super(PersonForm, self).__init__(*args, **kwargs)
# by default m2m relation is rendered with SelectMultiple widget -
# - below line is changing it to checkbox list
self.fields['marksheets'].widget = forms.CheckboxSelectMultiple()
class Meta:
model = Person
views.py
#inside your view function
if request.method == 'POST':
form = PersonForm(request.POST)
if form.is_valid():
form.save()
# pass form to template in render_to_response
add.html
<form method="post" action="/newdcl/{{page_name}}/add/" > {% csrf_token %}
{{ form }}
<input type="submit" value="save" >
</form>