Creating a dynamic Form Django - python

Consider these Django models:
class MonitorSession(models.Model):
agent = models.ForeignKey(Agent, on_delete=models.CASCADE)
date = models.DateTimeField()
contact_motive = models.ForeignKey(ContactMotive)
customer_number = models.CharField(max_length=65)
protocole_number = models.CharField(max_length=65)
strong_points = models.TextField(blank=True)
points_to_improve = models.TextField(blank=True)
action_plan = models.TextField(blank=True)
def __unicode__(self):
return u"%s, %s" % (self.customer_number, self.protocole_number)
class EvaluationCategory(models.Model):
cel = models.ForeignKey(Cel, on_delete=models.CASCADE)
category = models.CharField(max_length=65)
description = models.TextField(blank=True)
max_points = models.IntegerField()
def __unicode__(self):
return u"%s: %s" % (self.cel, self.category)
class EvaluationItem(models.Model):
category = models.ForeignKey(EvaluationCategory, on_delete=models.CASCADE)
item = models.CharField(max_length=65)
def __unicode__(self):
return u"%s: %s" % (self.category, self.item)
class EvaluationScore(models.Model):
monitor_session = models.ForeignKey(MonitorSession, on_delete=models.CASCADE)
item = models.ForeignKey(EvaluationItem, )
score = models.ForeignKey(PossibleScore, on_delete=models.CASCADE)
def __unicode__(self):
return u"%s: %s" % (self.item, self.score)
Now I need to create a form to with these all the fields from the MonitorSession class.
After those fields I would need to create additional form fields which are dynamic, and would be returning from this query:
fields = EvaluationItem.objects.all().order_by(EvaluationCategory__category)
As you can Imagine the number of fields is not known and is dynamic.
Is there an automated way in Django to get this done? Ar will I have to create the form manually and for the Item fields a loop? I have been trying to get this done with Formsets but I do not see how formsets could help me in this.

The above suggestion is giving me part of the solution: This would be the code of my Form:
class MonitorSessionForm(forms.Form):
agent = forms.ModelChoiceField(queryset=Agent.objects.all())
date = forms.DateField()
contact_motive =forms.ModelChoiceField(queryset=ContactMotive.objects.all())
customer_number = forms.CharField(max_length=65)
protocole_number = forms.CharField(max_length=65)
strong_points = forms.CharField(widget=forms.Textarea)
points_to_improve = forms.CharField(widget=forms.Textarea)
action_plan = forms.CharField(widget=forms.Textarea)
def __init__(self, *args, **kwargs):
extra = kwargs.pop('extra')
super(MonitorSessionForm, self).__init__(*args, **kwargs)
for i, item in enumerate(extra):
self.fields['custum_%s' % i] = forms.CharField(label=item)
My template code:
{% load bootstrap3 %}
<h2>Fazer Monitoria</h2>
<div class="col-md-6">
<form method="post" action="">
{% csrf_token %}
{% bootstrap_form form %}
<br><br>
<input type="submit" name="submit" value="Salvar Monitoria" class="btn btn-primary">
</form>
<br><br>
</div>
And my View code:
#login_required()
def add_monitorsession(request):
items = EvaluationItem.objects.all()
if request.method == 'POST':
form = MonitorSessionForm(request.POST, extra=items)
if form.is_valid():
print u'Tudo ok!'
else:
form = MonitorSessionForm
return render_to_response('add_monitorsession.html', {'form': form})
Trying to render this is giving me a key error:
KeyError at /addmonitorsession/
'extra'
Request Method: GET
Request URL: http://127.0.0.1:8000/addmonitorsession/
Django Version: 1.9.6
Exception Type: KeyError
Exception Value:
'extra'
Exception Location: ..../monitoria_altocontato/main/forms/monitor_session.py in __init__, line 19
pointing out to the next line of the form
extra = kwargs.pop('extra')
Any one an idea?

Related

Django: Reverse for 'add_review' with arguments '('',)' not found. 1 pattern(s) tried: ['movies/addreview/(?P<id>[0-9]+)/\\Z']

I want to add review function in movie_detail.html, but I don't know how to query comments and map to movie_detail.It returns Reverse for 'add_review' with arguments '('',)' not found error.
My url.py:
urlpatterns = [
path('', MovieList.as_view(), name='movie_list'),
path('<int:pk>', MovieDetail.as_view(), name='movie_detail'),
path('search/', MovieSearch.as_view(), name='movie_search'),
path('addreview/<int:id>/', views.add_review, name='add_review'),
]
My model.py:
class Movie(models.Model):
title = models.CharField(max_length=200)
actors = models.CharField(max_length=500, null=True)
poster_url = models.CharField(max_length=200)
director = models.CharField(max_length=200, default='')
score = models.FloatField()
genres = models.CharField(max_length=200)
language = models.CharField(max_length=200, default='English')
durations = models.IntegerField(default=0)
regions = models.CharField(max_length=200, default='')
release_date = models.DateField(default=timezone.now)
description = models.TextField(max_length=1000, default='')
year = models.IntegerField(default=2000)
views_count = models.IntegerField(default=0)
created = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
def genres_as_list(self):
genre_list = self.genres.split('/')
return genre_list
def actors_as_list(self):
return self.actors.split('/')
class Review(models.Model):
movie = models.ForeignKey(Movie, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
comments = models.TextField(max_length=1000)
rating = models.FloatField(default=0)
data_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.username
my view.py:
class MovieDetail(DetailView):
model = Movie
template_name = "main/movie_detail.html"
def get_object(self):
object = super().get_object()
object.views_count += 1
object.save()
return object
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['links'] = MovieLinks.objects.filter(movie=self.get_object())
context['related_movies'] = Movie.objects.filter(genres__in=self.get_object().genres_as_list()).order_by(
'created')[0:6]
# context['reviews'] = Review.objects.filter(id=self.kwargs['pk'])
return context
class MovieSearch(ListView):
model = Movie
paginate_by = 20
template_name = "main/movie_list.html"
def get_queryset(self):
query = self.request.GET.get('query')
if query:
object_list = self.model.objects.filter(title__icontains=query)
else:
object_list = self.model.objects.none()
return object_list
#login_required
def add_review(request, id):
movie = Movie.objects.get(id=id)
if request.method != "POST":
form = ReviewForm(request.POST or None)
if form.is_valid():
data = form.save(commit=False)
data.comment = request.POST["comment"]
data.rating = request.POST["rating"]
data.user_id = request.user.pk
data.movie_id = movie.pk
data.save()
return redirect("main:movie_detail", id)
else:
form = ReviewForm()
context = {"form": form}
return render(request, 'main/movie_detail.html', context)
my movies_detail.html review part:
<section class="comments">
<h3>Comment</h3>
<div class="card">
<div class="card-body">
<h3 class="text-center">Add Review</h3>
<form method="POST" action="{% url 'main:add_review' movie.id%}">
{% csrf_token %}
<label for="comment">Review</label>
<textarea name="comment" id="comment" cols="30" rows="5" class="form-control"></textarea>
<label for="rating">Rating</label>
<input type="text" name="rating" class="form-control">
<br>
<input type="submit" class="btn btn-danger" value="Add Review">
</form>
</div>
</div>
I want add the review function and I code the add_review in view.py by some tutorial.But I don't know how to fix it.
You are not passing the movie object to template, so movie.id evaluates to an empty to string, which causes the exception you are seeing.
Try this:
#login_required
def add_review(request, id):
movie = Movie.objects.get(id=id)
...
context = {"form": form, "movie": movie}
return render(request, 'main/movie_detail.html', context)
Edit: alternatively, you can just set action="" as shah sawood pointed out in his answer.
On a side note: Welcome to Stack Overflow! Next time, please post the full stacktrace, that increases your chances of getting an answer to your problem :)
Note
When you want to post data a view that it renders you need not to specify action but rather leave as action=""
You're getting the error because you're not passing a movie to the context in the last 2 lines of add_review. You need to update it to
context = {"form": form, "movie": movie}
return render(request, 'main/movie_detail.html', context)

Unable to get user.id from into django form

I'm unable to get user.school.id into the form shown below.
I have not been able to know the reason as to why this is happening.
Below is my forms.py
class StudentsForm(forms.ModelForm):
class Meta:
model = StudentsModel
fields = ("school","adm","name","kcpe","form","stream","gender","notes")
widgets = {
'school':forms.TextInput(attrs={"class":'form-control','value':'','id':'identifier','type':'hidden'}),
"adm":forms.TextInput(attrs={"class":'form-control'}),
"name":forms.TextInput(attrs={"class":'form-control'}),
"form":forms.Select(choices=class_forms,attrs={"class":'form-control'}),
"stream":forms.Select(choices=streams,attrs={"class":'form-control'}),
"gender":forms.Select(choices=gender, attrs={"class":'form-control'}),
}
Below is the script from the template where the id is to reflect.
<script>
document.getElementById('identifier').value = '{{ user.school.id }}';
</script>
And this is the Students model
class StudentsModel(models.Model):
school = models.ForeignKey(School,on_delete=models.CASCADE)
adm = models.CharField(max_length=200)
name = models.CharField(max_length=200)
form = models.ForeignKey(FormModel, on_delete=models.CASCADE)
stream = models.ForeignKey(StreamModel,on_delete=models.CASCADE)
gender = models.ForeignKey(GenderModel,on_delete=models.CASCADE)
def __str__(self):
return "%s | %s" % (self.name,self.adm)
Please help me out. If there's anything else I need to add let me know.
class School(models.Model):
name = models.CharField(max_length=100,default='default')
def __str__(self):
return str(self.name)
class User(AbstractUser):
school = models.ForeignKey(School, on_delete=models.DO_NOTHING, null=True, blank=True,default=1)
#role = models.CharField(max_length=10, choices=ROLES, blank=False, null=False)
is_student = models.BooleanField(default=False)
is_teacher = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
def __str__(self):
return (str(self.school) + ' | ' + self.username)
The view
class AddStudentView(LoginRequiredMixin,CreateView):
model = StudentsModel
form_class = StudentsForm
template_name = 'students.html'
success_url = reverse_lazy('students')
def get_context_data(self, *args, **kwargs):
streams = StreamModel.objects.filter(school=self.request.user.school)
students = StudentsModel.objects.filter(school=self.request.user.school)
forms = FormModel.objects.filter(school=self.request.user.school)
context = super(AddStudentView,self).get_context_data(*args, **kwargs)
context["streams"] = streams
context["students"] = students
context["forms"] = forms
return context
The form is here
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<button class="btn btn-primary">Add</button>
<span class="nav-item dropdown one " style="float:right">
Upload
</span>
</form>
I was able t solve this by creating two different views, one for createview and the other for listview. This worked.

Select a valid choice. That choice is not one of the available choices. Django Error

Hy, I am a beginner in django and I am trying to create an App like Quora. I want the user to answer based on the question selected which I get from list of items. I wrote both class based and function based views but I get the same error of "Select a valid choice. That choice is not one of the available choices."
Models.py
class Question(models.Model):
current_user = models.ForeignKey(User,on_delete=models.CASCADE)
question = models.TextField()
question_date_pub = models.DateTimeField(auto_now_add=True)
def __str__(self):
return (self.question)
def get_absolute_url(self):
return reverse('questions')
class Answer(models.Model):
current_user = models.ForeignKey(User,on_delete=models.CASCADE)
question = models.ForeignKey(Question,on_delete=models.CASCADE)
answer = models.TextField()
def __str__(self):
return self.answer
def get_absolute_url(self):
return reverse('questions')
Forms.py
from django import forms
from .models import Question,Answer
#
class AskQuestionForm(forms.ModelForm):
class Meta:
model = Question
fields = ['current_user','question']
widgets = {
'current_user':forms.TextInput(attrs={'class':'form-control','id':"my_user_input","type":"hidden"}),
'question':forms.Textarea(attrs={'class':'form-control'})
}
class AnswerQuestionForm(forms.ModelForm):
class Meta:
model = Answer
fields = ['current_user','question','answer']
widgets = {
'current_user':forms.TextInput(attrs={'class':'form-control','id':"my_user_input","type":"hidden"}),
'question':forms.TextInput(attrs={'class':'form-control','id':"current_question"}),
'answer':forms.Textarea(attrs={'class':'form-control'})
}
Views.py
def AnswerQuestionView(request,pk):
question = Question.objects.get(pk=pk)
if request.method == "POST":
form = AnswerQuestionForm(request.POST or None)
if form.is_valid():
form.save()
else:
form = AnswerQuestionForm()
return render(request,'answer_question.html',{'form':form,'question':question})
This is the class based view also giving the same error
class AnswerQuestionView(CreateView):
model = Answer
form_class=AnswerQuestionForm
template_name = "answer_question.html"
def get_context_data(self,*args,**kwargs):
context = super(AnswerQuestionView,self).get_context_data(*args,**kwargs)
question = get_object_or_404(Question,id=self.kwargs['pk'])
context['question'] = question
return context
Html File
{% extends 'base.html'%}
{% load static %}
<!--document.getElementById('current_question').value = "{{question}}";-->
{% block content %}
<div class="container">
<form method="POST">
{%csrf_token%}
{{form.as_p}}
<br>
<button type="submit" class="btn btn-primary">Answer Question</button>
</form>
</div>
<script>
var question_clicked = "{{question}}"
var name = "{{user.id}}"
document.getElementById('my_user_input').value = name;
document.getElementById('current_question').value =question_clicked;
</script>
{% endblock content%}

Passing initial value in formset

Models
attendance_choices = (
('absent', 'Absent'),
('present', 'Present')
)
class Head_of_department(models.Model):
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
email = models.CharField(max_length=30)
def __str__(self):
return self.first_name
class Employee(models.Model):
first_name = models.CharField(max_length=200, unique=True)
last_name = models.CharField(max_length=200, unique=True)
head_of_department = models.ForeignKey('Head_of_department', on_delete=models.SET_NULL, blank=True, null=True)
email = models.EmailField(max_length=100)
def __str__(self):
return self.first_name + ' ' + self.last_name
class Attendance(models.Model):
head_of_department = models.ForeignKey('Head_of_department', on_delete=models.SET_NULL, blank=True, null=True)
employee = models.ForeignKey('Employee', on_delete=models.CASCADE, )
attendance = models.CharField(max_length=8, choices=attendance_choices, blank=True)
Views
class Attendancecreate(CreateView):
model = Attendance
fields = ['employee']
success_url = '/dashboard/'
def get_context_data(self,** kwargs):
context = super(Attendancecreate, self).get_context_data(**kwargs)
context['formset'] = AttendanceFormset(queryset=Attendance.objects.none(), instance=Head_of_department.objects.get(email=email), initial=[{'employee': employee} for employee inself.get_initial()['employee']])
context['attendance_form'] = Attendanceform()
email = self.request.user.email
hod = Head_of_department.objects.get(email=email)
context["employees"] = Employee.objects.filter(head_of_department =hod)
return context
def get_initial(self):
email = self.request.user.email
hod = Head_of_department.objects.get(email=email)
initial = super(Attendancecreate , self).get_initial()
initial['employee'] = Employee.objects.filter(head_of_department=hod)
return initial
def post(self, request, *args, **kwargs):
formset = AttendanceFormset(queryset=Attendance.objects.none(), instance=Head_of_department.objects.get(email=email), initial=[{'employee': employee} for employee inself.get_initial()['employee']))
if formset.is_valid():
return self.form_valid(formset)
def form_valid(self, formset):
instances = formset.save(commit=False)
for instance in instances:
instance.head_of_department = get_object_or_404(Head_of_department, email=self.request.user.email)
instance.save()
return HttpResponseRedirect('/dashboard/')
def form_invalid(self, formset):
print ('errors')
print (formset.errors)
Forms
class Attendanceform(ModelForm):
class Meta:
model = Attendance
fields = ('employee','attendance','hod')
AttendanceFormset = inlineformset_factory(Head_of_department,Attendance,fields=('attendance',))
Template
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form }}
<br><br>
{% endfor %}
Error
Below shown square brackets was printed in the console when I used print(formset.errors)
[]
How to pass employees as initial values for attendance model as shown below in the images, employees will be rendered and rendered values have to be passed as employee in attendance model.
Is there any workaround with modelformset or inlineformset ? I can't get it right with the views I have included in the question .
I was missing request.post ,
class Attendancecreate(CreateView):
...
def post(self, request, *args, **kwargs,):
formset = AttendanceFormset(request.POST,queryset=Attendance.objects.none(), instance=Head_of_department.objects.get(email=self.request.user.email), initial=[{'employee': employee} for employee in self.get_initial()['employee']])
if formset.is_valid():
return self.form_valid(formset)
Template
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form.employee.initial }} {{ form.employee}} {{ form.attendance }}
<br><br>
{% endfor %}

Unable to automatically pick foreign key from modelform

I am working on a product app on Python 2.7 / Django 1.7.
I have a model for product namely 'product_profile' and I want to allow my customer (end user) to ask any thing regarding specific products using a form.
However I am unable to allow user to automatically select the product (foreign key) and the customer has to select from a drop-down which quite irrational. I have also assigned the foreign key in url-variable.
here is my code:
MODEL.PY
class ProductProfile(models.Model):
category = models.ForeignKey(Category)
brand = models.ForeignKey(Brand)
product_name = models.CharField(max_length=128)
model_name = models.CharField(max_length=128)
generation = models.CharField(max_length=128)
processor = models.CharField(max_length=128)
ram = models.DecimalField(max_digits=2, decimal_places=0)
hdd = models.DecimalField(max_digits=6, decimal_places=2)
optical_drive = models.CharField(max_length=128)
display = models.CharField(max_length=128)
card_reader = models.CharField(max_length=128)
blue_tooth = models.CharField(max_length=128)
web_cam = models.CharField(max_length=128)
warranty = models.CharField(max_length=128)
price = models.DecimalField(max_digits=9, decimal_places=2)
condition = models.TextField()
product_image = models.ImageField(upload_to=update_Product_image_filename)
post_date = models.DateTimeField(db_index=True, auto_now_add=True)
# Override th __unicode__() method to return out something meaningful!
def __unicode__(self):
return self.product_name
class Customer_ps_contact(models.Model):
name = models.CharField(max_length=128)
email = models.EmailField(max_length=75)
subject = models.CharField(max_length=128 )
product = models.ForeignKey(ProductProfile)
message = models.TextField()
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format:
'+999999999'. Up to 15 digits allowed.")
phone_number = models.CharField(validators=[phone_regex], blank=True, max_length=15) # validators should be a
list
def __unicode__(self):
return self.name
FORM.PY
class Customer_ps_contactForm(forms.ModelForm):
class Meta:
model = Customer_ps_contact
product = forms.ModelChoiceField(queryset=ProductProfile.objects.all(),
widget=forms.HiddenInput())
fields = ('name','email', 'product','subject','message', 'phone_number')
VIEWS.PY
def product_inquiry(request, product_id):
product = ProductProfile.objects.get(pk=product_id)
if request.method == 'POST':
#form = Customer_ps_contactForm(request.POST, initial = {'product': product})
#form = Customer_ps_contactForm(initial = {'product': product.id})
form = Customer_ps_contactForm(request.POST)
if form.is_valid():
form_data_dict = form.cleaned_data
print form_data_dict['product']
mail_customer_enquriy(form_data_dict) # Function to send email to admin
thank_u_customer(form_data_dict) # Function to send email to customers
form = form.save(commit=False)
form.product = product
form.save()
return home(request)
else:
print ("form is not valid")
print (form.errors)
else:
form = Customer_ps_contactForm()
context_dict = {'form':form, 'product': product}
return render(request, 'product/product_inquiry2.html',context_dict)
URL Patterns
urlpatterns = patterns('',
url(r'^inquiry/(?P<product_id>\d+)/$', views.product_inquiry, name='price'), # Only relevent url given
)
Template : product_inquiry2.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block body_block %}
{% block title %}Product Inquiry{% endblock %}
<div class="row">
<div class="col-md-10 col-md-offset-1">
<h2 style="font-weight:bold">Enquiry regarding '{{product.product_name}}'</h2>
<hr>
<form id="contact_form" method="post" action=""/>
{% csrf_token %}
{{ form | crispy }}
<input class="btn btn-primary pull-right " type="submit" name="submit" value="Submit the Message" />
</form>
</div>
</div>
{% endblock %}
What should I do?
You know what the product is from the id in the url, so there's no need to include it in your form.
To check that the product exists in the database, you can use the get_object_or_404 shortcut.
def product_inquiry(request, product_id):
product = get_object_or_404(ProductProfile, pk=product_id)
Then leave out 'product' from your list of fields, and remove the ModelChoiceField with hidden input widget.
class Customer_ps_contactForm(forms.ModelForm):
class Meta:
model = Customer_ps_contact
fields = ('name','email','subject','message','phone_number')
You are already setting the product when you save it, but it would be clearer to use the variable name instance to make it clearer what's going on. If you change your mail_customer_enquriy and thank_u_customer methods to use the instance instead of cleaned_data, then you won't have to do anything with form.cleaned_data.
if form.is_valid():
instance = form.save(commit=False)
instance.product = product
instance.save()
mail_customer_enquriy(instance) # Function to send email to admin
thank_u_customer(instance) # Function to send email to customers
return home(request)

Categories

Resources