Creating django form for related models - python

I have the following models defined:
class Question(models.Model):
date_added = models.DateTimeField(auto_now_add=True)
question = models.CharField(max_length=200)
number_of_answers = models.IntegerField(default=0)
class Answer(models.Model):
question = models.ForeignKey(Question)
answer = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
I want the user to be able to create a question along with its corresponding answers.
Basically, I want to create a form that will ask the user to enter a question and the number of answers for the question. Based on the number-of-answers specified, I want to then create that many text fields for each answer. I want to be able to connect each answer to its corresponding question in the database.
What is the best way to do this in Python Django? I have provided images of what this would look like visually.
Getting question and number of answers,
getting answers based on question and number of answers specified

I would tackle this problem by using modelForms for creating questions, and then redirecting to a page where you can add the number of questions you specified when you created the question. This add_answers page won't use Django forms, we can just use a simple html form and then get the form data in the view. Here are the views:
views.py:
from django.shortcuts import render, redirect
from .forms import QuestionForm
from .models import Question, Answer
# Create your views here.
def add_question(request):
form = QuestionForm(request.POST or None)
if request.method == "POST":
if form.is_valid():
question = form.cleaned_data.get('question')
number_of_answers = form.cleaned_data.get('number_of_answers')
create_question = Question.objects.create(question=question, number_of_answers=number_of_answers)
create_question.save()
return redirect('home:add-answers', id=create_question.id)
return render(request, 'home/add_question.html', {'form': form})
def add_answers(request, id):
question = Question.objects.get(id=id)
if request.method == "POST":
for i in request.POST.getlist('answers'):
_ = Answer.objects.create(answer=i, question=id)
_.save()
num_answers = question.number_of_answers
context = {"num_answers":range(num_answers), 'question':question}
return render(request, 'home/add_answers.html', context)
add_question uses model forms:
forms.py:
from django import forms
from .models import Question, Answer
class QuestionForm(forms.ModelForm):
class Meta:
model = Question
fields = ['question','number_of_answers']
We can access this form in the template:
<form method="POST" action=".">
{{ form }}
{% csrf_token %}
<input type="submit">Submit</input>
</form>
For add_answers, we do the following:
<form method="POST" action=".">
{% for i in num_answers %}
<input type="text" name="answers"></input>
<br />
{% endfor %}
{% csrf_token %}
<input type="submit">Submit</input>
</form>
Where num_answers is a context variable that is range(number_of_answers).
Edit: here is the urls.py file:
from django.conf.urls import url, include
from . import views
urlpatterns = [
url(r'^add-answers/(?P<id>\d+)/$', views.add_answers, name="add-answers"),
url(r'^add-question/$', views.add_question, name="add-question"),
]

Have a look of formsets.And combine with django-dynamic-forms can help you custom a web page to add question with answer.
Here is a demo of add a team with it's player.
models.py
class Player(models.Model):
name = models.CharField(max_length=50)
score = models.IntegerField()
age = models.IntegerField()
def __str__(self):
return self.name
class Team(models.Model):
name = models.CharField(max_length=100)
players = models.ManyToManyField(Player)
def __str__(self):
return self.name
forms.py
from django import forms
from django.forms.formsets import formset_factory
from .models import *
class PlayerForm(forms.ModelForm):
class Meta:
model = Player
fields = '__all__'
PlayerFormset = formset_factory(PlayerForm)
class TeamForm(forms.Form):
name = forms.CharField()
players = PlayerFormset()
views.py
from django.shortcuts import render
from .forms import *
from .models import *
def post(request):
if request.method == 'POST':
form = TeamForm(request.POST)
player_instances = PlayerFormset(request.POST)
if form.is_valid():
if player_instances.is_valid():
team = Team(name=form.cleaned_data['name'])
team.save()
args = {'form': form}
for item in player_instances:
if item.is_valid():
player = item.save()
team.players.add(player)
else:
print('-----------error occur')
team.save()
return render(request, 'app1.html', args)
args = {'form': form}
return render(request, 'app1.html', args)
else:
form = TeamForm()
args = {'form': form}
return render(request, 'app1.html', args)
app1.html
<html>
<head>
<title>gffdfdf</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="/static/jquery.formset.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<form id="myForm" action="" method="post" class="">
{% csrf_token %}
<h2> Team</h2>
{% for field in form %}
{{ field.errors }}
{{ field.label_tag }} : {{ field }}
{% endfor %}
{{ form.players.management_form }}
<h3> Product Instance(s)</h3>
<table id="table-product" class="table">
<thead>
<tr>
<th>player name</th>
<th>highest score</th>
<th>age</th>
</tr>
</thead>
{% for player in form.players %}
<tbody class="player-instances">
<tr>
<td>{{ player.name }}</td>
<td>{{ player.score }}</td>
<td>{{ player.age }}</td>
</tr>
</tbody>
{% endfor %}
</table>
<button type="submit" class="btn btn-primary">save</button>
</form>
</div>
<script>
$(function () {
$('#myForm tbody tr').formset();
})
</script>
</body>
</html>
screen like:

Related

Getting NoReverseMatch while click the comment button

I was getting that same error while click the like button, But the error was solved..
again after creating comment view and its other staff I'm getting that error again...When I click the comment button then the error appears..I'm very new to Django,,, help me please..
My project models.py, template page, urls.py, views.py are attached herewith
**models.py**
from email.policy import default
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Blog(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200, verbose_name="Put a Title")
blog_content = models.TextField(verbose_name="What is on your mind")
blog_image = models.ImageField(upload_to="blog_images", default = "/default.png")
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
class Comment(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name = "blog_comment" )
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name = "user_comment")
comment = models.TextField()
comment_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.comment
class Like(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name = "blog_liked")
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name = "user_liked")
class Unlike(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name = "blog_unliked")
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name = "user_unliked")
**blog_page.html**
{% extends "main.html" %}
{% load static %}
{% load crispy_forms_tags %}
{% block content %}
<div style="text-align:center;">
<h2>{{blog.title}}</h2>
<img src="{{blog.blog_image.url}}" alt="" width="630px" height="300px">
</div>
<div style="text-align:center;">
{{blog.blog_content|linebreaks}}
</div>
{% if not liked and not unliked %}
<h4> Like </h4>
<h4>Unlike</h4>
{% elif unliked %}
<h4> Like </h4>
{% elif liked %}
<h4>Unlike</h4>
{% endif %}
<div>
<h4>
Comments:
</h4>
{% for comment in comments %}
<div>
{{ user }} <br>
<h5>{{ comment }}</h5>
</div>
{% endfor %}
<!-- <h6>Add your comment:</h6> -->
<form action="" method="POST">
{% csrf_token %}
{{form|crispy}} <br>
<a class="btn btn-sm btn-info" href="{% url 'comment' %}">Comment</a>
</form>
</div>
{% endblock content %}
**urls.py**
from django.urls import path
from blog_app import views
urlpatterns = [
path("", views.home, name='home'),
path("blog_page/<str:pk>/", views.blog_view, name='blog_page'),
path("like/<str:pk>/", views.like, name="like"),
path("unlike/<str:pk>/", views.unlike, name="unlike"),
path("comment/", views.comment, name="comment"),
]
**views.py**
from django.shortcuts import render
from . models import Blog, Comment, Like, Unlike
from . forms import CommentForm
# Create your views here.
def home(request):
blogs = Blog.objects.all()
context = {'blogs': blogs}
return render(request, 'blog_app/home.html', context)
def blog_view(request, pk):
blog = Blog.objects.get(id=pk)
form = CommentForm()
comments = Comment.objects.filter(blog=blog)
context = {"blog": blog, "comments": comments, "form":form}
return render(request, 'blog_app/blog_page.html', context)
def like(request, pk):
blog = Blog.objects.get(id=pk)
user = request.user
liked, like = Like.objects.get_or_create(blog=blog, user=user)
context = {"liked" : liked, "blog": blog }
return render(request, "blog_app/blog_page.html", context)
def unlike(request, pk):
blog = Blog.objects.get(id=pk)
user = request.user
unliked, unlike = Unlike.objects.get_or_create(blog=blog, user=user)
context = {"unliked" : unliked, 'blog': blog}
return render(request, "blog_app/blog_page.html", context)
def comment(request):
form = CommentForm()
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
form.save()
context = {}
return render(request, "blog_app/blog_page.html", context)
Your comment button is just a link, is it normal ? I think, you want to submit your form when you click on?
<div>
<h4>
Comments:
</h4>
{% for comment in comments %}
<div>
{{ user }} <br>
<h5>{{ comment }}</h5>
</div>
{% endfor %}
<!-- <h6>Add your comment:</h6> -->
<form action="{% url 'comment' %}" method="POST">
{% csrf_token %}
{{form|crispy}} <br>
<button type="submit" class="btn btn-sm btn-info">Comment</button>
</form>
</div>
And i think, your problem occured because you dispolay this template from comment view without set blog in context data.
def blog_view(request, pk):
blog = Blog.objects.get(id=pk)
form = CommentForm()
comments = Comment.objects.filter(blog=blog)
context = {"blog": blog, "comments": comments, "form":form}
return render(request, 'blog_app/blog_page.html', context)
def comment(request):
form = CommentForm()
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
form.save()
return redirect("blog_page", pk=form.instance.blog.pk)
return HttpResponse(status_code=400) # error case
else:
return HttpResponse(status_code=501) # try to GET page
Better solution is to pass blog pk in the url for being able to render page with error:
path("blog/<int:pk>/comment/", views.comment, name="comment")
<form action="{% url 'comment' blog.pk %}" method="POST">
{% csrf_token %}
{{form|crispy}} <br>
<button type="submit" class="btn btn-sm btn-info">Comment</button>
</form>
def comment(request, pk):
blog = get_object_or_404(Blog, pk=pk)
form = CommentForm()
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
form.save()
return redirect("blog_page", pk=blog.pk)
return render(request, "...", {"blog": blog, "form": form})

Django Form is not updating the user database

I want to update User database using forms.When I trying to update it remains same the database and not update. So to perform this task ?
forms.py
from django import forms
from django.contrib.auth.models import User
class updateform(forms.ModelForm):
class Meta:
model=User
fields="__all__"
views.py
from django.contrib.auth.models import User
from .forms import updateform
#permission_required('is_superuser')#only superuser can update the data base
def upform(request,id):
emp=User.objects.get(id=id)
if request.method=='POST':
frm=updateform(request.POST,instance=emp)
if frm.is_valid():
frm.save()
return redirect('/')
else:
frm=updateform(instance=emp)
return render(request,'examp.html',{'frm':frm})
examp.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static '/examp.css' %}">
<style>
td,th{
border:1px solid;
text-align:center;
padding:10px;
}
</style>
</head>
<body>
{% include 'include/header.html' %}
<form action="/" method="POST">
{% csrf_token %}
{{ frm.as_p }}
<input type="submit" value="Submit">
</form>
</body>
</html>
How to update the database using this given form.
Why don't you use CBV generic views?
in yourapp/models.py:
from django.db import models
from django.urls import reverse
from django.contrib.auth import get_user_model
# const for get_absolute_url
#that's suffix will be using in your template names look at urls.py,
#if you need u should change it for smth here and in urls!
DETAIL_SUFFIX = '_detail'
class YourModel(models.Model):
name = models.CharField(default='new', max_length=6)
updated_by = models.ForeignKey(get_user_model(), on_delete=models.PROTECT,
related_name='get_upd_by_user')
def __str__(self):
return f'{self.name}'
def get_absolute_url(self):
return reverse(f'{self.__class__.__name__}{DETAIL_SUFFIX}', kwargs={"pk": self.pk})
You need to define the get_absolute_url method. Then reverse will works properly after you update.
in yourapp/views.py:
#views
from django.views.generic.edit import UpdateView
from django.views.generic import DetailView
# AAA
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import PermissionRequiredMixin
class ProjectSubnetDetailView(LoginRequiredMixin, DetailView):
model = YourModel
template_name = 'your_name_detail.html'
class YourNameUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
model = YourModel
template_name = 'your_name_edit.html'
fields = ('somefield', )
permission_required = ('yourapp.view_yourmodel','yourapp.change_yourmodel')
def form_valid(self, form): # Bind name of current user
form.instance.updated_by = self.request.user
return super().form_valid(form)
Instead of decorator #permission_required in CBV you can use PermissionRequiredMixin(I highly recommend reading about it in the official documentation). In short you must define only in this order:
(LoginRequiredMixin, PermissionRequiredMixin, UpdateView)
First - user must be Authenticated
Second - user must have a rights (or roles, that have a need rights)
Third - thats your generic view for do smth (in this example is update)
For second Attention You must specify in the view those rights that are checked before performing the update in strictly lowercase.
You can define rights in admin panel for some user or create a role and define some rights and add your user into that role. For example i create user and define him some rights for view and change table:
So if your model is called 'YourModel', you should specify 'action_yourmodel'
permission_required = ('yourapp.view_yourmodel','yourapp.change_yourmodel')
in yourapp/urls.py.
from django.urls import path
from .views import YourNameUpdateViewUpdateView, YourNameDetailView
urlpatterns = [
path('YourName/<int:pk>/edit/', YourNameUpdateView.as_view(), name='some_name_edit'),
path('YourName/<int:pk>/detail/', YourNameDetailView.as_view(), name='some_name_detail'),
]
in yourapp/templates/ you have to define 2 templates:
#your_name_detail.html
{% extends '_basetemplate.html' %}
{% block content %}
<div>
<p>{{ object.name }}</p>
</div>
<p>
Update |
</p>
{% endblock content %}
# your_name_edit.html
{% extends '_basetemplate.html' %}
{% block content %}
<h5>Updating {{ object.name }}</h5>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-sm btn-info ml-2" type="submit">Update</button>
</form>
{% endblock content %}

How to make form html for updateview

I was making my own forms for CreateView and UpdateView with my html file because I don't want to display the form like this {{form.as_p}}.
forms.py
from django import forms
from .models import Post
class PostCreationForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'cover', 'text',)
widgets = {
'title': forms.TextInput(attrs={'class': 'title'}),
'cover': forms.FileInput(attrs={'class': 'image'}),
'text': forms.TextInput(attrs={'class': 'text'})
}
class PostDeleteForm(forms.ModelForm):
class Meta:
model = Post
fields = ('__all__')
views.py
from django.shortcuts import reverse
from django.http import HttpResponseRedirect
from django.views import generic
from .models import Post
from .forms import PostCreationForm, PostDeleteForm
class PostListView(generic.ListView):
model = Post
context_object_view = 'post_list'
template_name = 'forum/post_list.html'
class PostDetailView(generic.DetailView):
model = Post
context_object_view = 'post'
template_name = 'forum/post_detail.html'
class PostCreateView(generic.CreateView):
model = Post
form_class = PostCreationForm
template_name = 'forum/post_create.html'
def form_valid(self, form):
if form.is_valid():
response = form.save(commit = False)
response.author = self.request.user
response.save()
return HttpResponseRedirect(reverse('post_detail', args=[str(response.id)]))
class PostUpdateView(generic.UpdateView):
model = Post
context_object_view = 'post'
form_class = PostCreationForm
template_name = 'forum/post_edit.html'
def get_post(self, pk):
return get_object_or_404(Post, pk=pk)
def form_valid(self, form):
if form.is_valid():
response = form.save(commit = False)
response.save()
return HttpResponseRedirect(reverse('post_detail', args=[str(response.id)]))
class PostDeleteView(generic.DeleteView):
model = Post
context_object_view = 'post'
form_class = PostDeleteForm
template_name = 'forum/post_delete.html'
success_url = '/'
def get_post(self, pk):
return get_object_or_404(Post, pk=pk)
post_create.html
{% extends '_base.html' %}
{% block css %}
{% load static %}
<link rel="stylesheet" href="{% static 'css/post_create.css' %}">
{% endblock css %}
{% block title %}Questions{% endblock title %}
{% block content %}
<h1 class="m-title">New Post</h1>
<div class="form">
<form action="" method="post" enctype="multipart/form-data" id='postform'>
{% csrf_token %}
<p class="n-title"><label for="id_title">Title: </label></p>
<input id="id_title" type="text" name="title" class="title" maxlength="40" required>
<p class="n-image"><label for="id_cover">Image: </label></p>
<input id="id_cover" type="file" name="cover" class="image" required>
<p class="n-text"><label for="id_text">Text: </label></p>
<textarea id="id_text" placeholder="Enter your text here" name="text" class="text" form='postform' required></textarea>
<button class="btn btn-success" id="button" type="submit">Submit</button>
</form>
</div>
{% endblock content %}
And I also wanted to do for the UpdateView, but I don't know how to make the input fields display the current value of the post (title, text). The html file is the same as for CreateView. How do I make the input display the current value of the post that is being modified?
You probably don't want to be manually writing the html for the form like that.
If you must, you can pass in value like:
<input id="id_title" type="text" name="title" class="title" maxlength="40" value="{{ form.title.value }}" required>
Better would be to render the field using django so that your form field attributes like maxlength and required match your form class specification. For example, this will create the input for your title field:
{{ form.title }}
If you want more flexibility in styling look at crispy-forms or my preference floppyforms

Generate objects in django template dynamically

I have 2 models in my system:
class Display(models.Model):
name = models.CharField
UE = models.CharField
description
class Register(models.Model):
temp1 = models.FloatField()
temp2 = models.FloatField()
flow = models.FloatField()
I create displays using for inside a template, but the value of each display is a respective field in Register model. I can't make the loop with Register because i use only row (i can't loop fields). Understand?
Take a look of my code:
View:
def main(request):
dp_col = Display.objects.all()
reg = Registers.objects.latest('pk')
context = {
'dp_col': dp_col,
'reg':reg
}
return render(request,'operation.html',context)
Template:
{% for dp in dp_col %}
<div class='col-md-6'>
<div class="display-content">
<div class="display-data">
<h3 class="text-center display-desc">{{dp.name}}
<span>:</span>
<span class="text-center display-value">I need put the value of each field here</span>
<span class='display-unit'> {{dp.UE}}</span>
</h3>
</div>
</div>
</div>
{% empty %}
<!--colocar alguma coisa aqui, caso não tenha nada no for-->
{% endfor %}
Any ideas?
Thanks a lot!
This can be easily solved by using a Django Forms:
yourapp/forms.py
from django import forms
class DisplayForm(forms.ModelForm):
class Meta:
model = Display
fields = '__all__'
yourapp/views.py
from .forms import DisplayForm
def main(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = DisplayForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = DisplayForm()
return render(request, 'operation.html', {'form': form})
In operations.html:
<form method="post" action="">
{{ form }}
</form>
Or if you want custom html in each field:
<form method="post" action="">
{% for field in form %}
{{ field.label_tag }} {{ field }}
{% endfor %}
</form>
Reference:
https://docs.djangoproject.com/en/1.11/topics/forms/

How create a custom form in django with database values

model.py
class Venue(models.Model):
venue_Name = models.CharField(max_length=100)
place = models.CharField(max_length=50)
rent = models.IntegerField()
parking_area = models.IntegerField()
class Decoration(models.Model):
rate = models.IntegerField()
I have printed the values in database as radio buttons what i want to do is that i want to get the total sum i.e venue.rent + decoration.rate and print it in another page What shoud i give in form action I'm not that familiar with forms.
html
<form action="{% %}" method="post">
{% for venue in venues %}
<input type="radio" name=venue value=venue.rent />{{ venue.venue_Name}}
{% endfor %}
{% for decoration in decorations %}
<input type="radio" name=decor value=decoration.rate />{{ decoration.rating }}
{% endfor %}
<input type="submit" value=" " />
</form>
what should i write in view and urls to get the sum
You can use Django's form for validation and parsing. For that you would set up a form like so:
forms.py
from django import forms
class TotalSumForm(forms.Form):
venue = forms.ModelChoiceField(queryset=Venue.objects.all(), required=True)
decoration = forms.ModelChoiceField(
queryset=Decoration.objects.all(), required=True)
def get_total(self):
# send email using the self.cleaned_data dictionary
return self.cleaned_data['venue'].rent +\
self.cleaned_data['decoration'].rate
And then using a class based view, add the result to context upon submission.
views.py
from myapp.forms import TotalSumForm(
from django.views.generic.edit import FormView
class TotalCost(FormView):
template_name = 'your_template.html'
form_class = TotalSumForm
success_url = '/thanks/'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
total_result = form.get_total()
# return back to your_template.html with the total in context
return self.render_to_response(self.get_context_data(
form=form, total=total_result))
The urls are pretty simple:
urls.py
from django.conf.urls import patterns, url
import myapp.views
urlpatterns = patterns(
'',
url(r'^total_calc/$', myapp.views.TotalCost.as_view(), name='calculate_total'),
)
Your html could be modified like so
your_template.html
<html>
<body>
<h1>TEST SUCCESFULL!</h1>
{% if total %}
<p>Total cost for venue and decoration: {{ total }}</p>
{% endif %}
<form action="{% url 'calculate_total' %}" method="post">
{{ form.as_p }}
<input type="submit" value="Calculate Total" />
</form>
</body>
</html>

Categories