how to save many to many field using django custom forms - django

how can i save many courses to the student table .I want to keep my design like this.This code is not saving the many to many field(courses) through AddStudentForm.It returns an error with courses variable.If i used CharField instead of ManyToManyField in models for courses then the code works perfectly,but when i use ManyToManyField then it is not working.
it throws courses when i used form.errors .If i didn't use form.errors then it doesn't give any error neither saves the data.
how can i save many courses to the student table .I want to keep my design like this.This code is not saving the many to many field(courses) through AddStudentForm.It returns an error with courses variable.
models.py
class Course(models.Model):
title = models.CharField(max_length=250)
price = models.IntegerField(default=0)
duration = models.CharField(max_length=50)
def __str__(self):
return self.title
class Student(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField(Course)
email = models.EmailField()
image = models.ImageField(upload_to='Students',blank=True)
def __str__(self):
return self.name
forms.py
class AddStudentForm(forms.ModelForm):
# courses = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, queryset=Course.objects.all())
class Meta:
model = Student
fields = ['name','courses','email','image']
def __init__(self, *args, **kwargs):
super(AddStudentForm, self).__init__(*args, **kwargs)
self.fields["courses"].widget = CheckboxSelectMultiple()
self.fields["courses"].queryset = Course.objects.all()
views.py
def addstudent(request):
courses = Course.objects.all()
if request.method == 'POST':
form = AddStudentForm(request.POST,request.FILES)
if form.is_valid():
student = form.save(commit=False)
course = form.cleaned_data['courses']
student.courses = course
student.save()
# student.courses.add(course)
# student.save_m2m()
# student.courses.set(course) # this method also didn't helped me
messages.success(request, 'student with name {} added.'.format(student.name))
return redirect('students:add_student')
else:
# messages.error(request,'Error in form.Try again')
return HttpResponse(form.errors) # this block is called and returns courses
else:
form = AddStudentForm()
return render(request,'students/add_student.html',{'form':form,'courses':courses})
add_student.html
<form action="{% url 'students:add_student' %}"
method="post"
enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<h5>Full Name <span class="text-danger">*</span>
</h5>
<div class="controls">
<input type="text" name="name" class="form-
control" > </div>
</div>
<div class="form-group">
<h5>Courses<span class="text-danger">*</span>
</h5>
<div class="controls">
{% for course in courses %}
<input name ="courses" type="checkbox" id="course-
{{course.id}}" value="{{course.title}}">
<label for="course-{{course.id}}">{{course.title}}
</label>
{% endfor %} # i think the problem is here.
</div>
</div>
<div class="form-group">
<h5>Email <span class="text-danger">*</span></h5>
<div class="controls">
<input type="text" name="email" class="form-
control" required> </div>
</div>
</div>
<div class="form-group">
<h5>Image <span class="text-danger">*</span></h5>
<div class="controls">
<input type="file" name="image" class="form-control" > </div>
</div>
<div class="text-xs-right">
<button type="submit" class="btn btn-info">Add</button>
</div>
</form>

You need to save first before you can assign m2m, the system needs the primary key of the Student model before it can insert into the m2m table.
if form.is_valid():
student = form.save(commit=False)
course = form.cleaned_data['courses']
student.save()
# this will save by itself
student.courses.set(course)

Related

Django objection creation failing due to FOREIGN KEY constraint failure (IntegrityError )

My website has a comment section where user can leave comments on a product . The comments on the product page will be stored in a model called 'ProductReview'. Here is the code for the model :
class ProductReview(models.Model):
product = models.ForeignKey(Product, related_name='reviews', on_delete=models.CASCADE)
name = models.CharField(blank=True,max_length=20)
stars = models.IntegerField()
content = models.TextField(blank=True)
date_added = models.DateTimeField(auto_now_add=True)
created_by = models.OneToOneField(User, on_delete=models.CASCADE)
Now the view associated with the model are as follows Note:The entire view isnt relevant to the error. The part relevant to the saving comment is the second 'request.POST' which I have denoted with a python
comment using # :
def product(request, category_slug, product_slug):
cart = Cart(request)
product = get_object_or_404(Product, category__slug=category_slug, slug=product_slug)
if request.method == 'POST':
form = AddToCartForm(request.POST)
if form.is_valid():
quantity = form.cleaned_data['quantity']
cart.add(product_id=product.id, quantity=quantity, update_quantity=False)
messages.success(request, 'The product was added to the cart')
return redirect('product', category_slug=category_slug, product_slug=product_slug)
similar_products = list(product.category.products.exclude(id=product.id))
# this part is for saving of the user comments to productreview model
if request.method == 'POST':
stars = request.POST.get('stars', 3)
content = request.POST.get('content', '')
name = request.POST.get('name', '')
created_by = request.user
review = ProductReview.objects.create(product=product, name=name, stars=stars, content=content, created_by=created_by)
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
# this marks the end of the code relevant to saving the user comment
if len(similar_products) >= 4:
similar_products = random.sample(similar_products, 4)
user_type = 0
if request.user.is_authenticated:
user_type = UserType.objects.filter(created_by=request.user.id).values_list('user_type', flat=True)
user_type = int(user_type[0])
return render(request, 'product.html', {'product': product, 'similar_products': similar_products, 'user_type': user_type})
And finally the relevant part of the template 'product.html' which is referenced in the view
{% if request.user.is_authenticated %}
{% if user_type == 2 %}
<div class="notification space-below">
<form method="post" action=".">
{% csrf_token %}
<div class="field">
<label>Name</label>
<div class="control">
<input class="text" name="name" value="{{ request.user }}" readonly>
</div>
</div>
<div class="field">
<label>Stars</label>
<div class="control">
<div class="select">
<select name="stars">
<option value="1">1</option>
<option value="2">2</option>
<option value="3" selected>3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
</div>
</div>
<div class="field">
<label>Content</label>
<div class="control">
<textarea class="textarea" name="content"></textarea>
</div>
</div>
<div class="field">
<div class="control">
<button class="button is-success">Submit</button>
</div>
</div>
</form>
</div>
{% else %}
<div>sign in with a buyer account to leave review</div>
{% endif %}
{% endif %}
Now when I try to fill the form in the product.html page and try to submit it ,I get the following error:
FOREIGN KEY constraint failed
IntegrityError at /smartwatch/apple-watch/
Can anyone tell what exactly is the issue with my code?
Pretty sure this all comes to your model, especially to this single line:
created_by = models.OneToOneField(User, on_delete=models.CASCADE)
You are using OneToOneField. This means a single user will be able to leave a single review in your entire application. You are getting integrity errors probably because this user you've selected already has a review on a different product.
If this isn't what you want, use normal ForeignKey instead.
I think your intention was to ensure that a single user can leave only one review per product. In that case, you should try setting UniqueConstraint between the product and created_by fields

Django Custom Form Field Property not Available in Template

I'm writing a voting system that shows the number of votes for an option in the form.
I have Vote and Option models:
class Vote(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=False, null=False)
option = models.ForeignKey(Option, on_delete=models.PROTECT, blank=False, null=False)
class Option(models.Model):
prompt = models.TextField(blank=False, null=False)
I wrote a simple custom form field to hold the current number of votes:
class VoteField(forms.BooleanField):
vote_count = 0
def __init__(self, *args, option, **kwargs):
self.vote_count = Vote.objects.filter(option=option).count()
super(VoteField, self).__init__(*args, **kwargs)
I have a form to create a list of options to vote for:
class VoteForm(forms.BaseForm):
base_fields = {}
def __init__(self, *args, options, user, **kwargs):
for options in options:
field = VoteField(option=option)
field.label = option.prompt
self.base_fields["option" + str(option.pk)] = field
super(SurveyResponseForm, self).__init__(*args, **kwargs)
And I have a template to show the voting form:
{% for field in form %}
<div class="row">
<div class="col">
{{ field }}
</div>
<div class="col">
{{ field.label }}
</div>
<div class="col">
{{ field.vote_count }}
</div>
</div>
{% endfor %}
I populated the db with an option and some responses. In the shell it works as expected:
>>> option = Option.objects.get(pk=1)
>>> vote_field = VoteField(option=option)
>>> vote_field.vote_count
3
However, my template outputs nothing where I say to print vote_count:
<div class="row">
<div class="col">
<input type="checkbox" name="option-1" id="id_option-1">
</div>
<div class="col">
First Option
</div>
<div class="col">
</div>
</div>
Am I doing something wrong here?
The "fields" that are accessed via form.<field_name> or for field in form are actually instances of BoundField. These BoundField objects are a combination of the form data and the field defined on the Form class, they are not references to the field defined on the form
To access the actual field use field.field
{{ field.field.vote_count }}

django form not submitting due to slug and foreign key

My django cabin form is not submitting. I have applied foreign key and slug in my cabin's model and after that my form stopped getting submitted. whenever I enter all the fields and hit submit button,the form page is getting reloaded and no data is getting submitted.Most Importantly it is not showing any error so that I can fix it.I have tried and searched a lot about it and tried different things but still it didn't work. I am stuck to it. Please help!!
class Centre(models.Model):
name= models.CharField(max_length=50, blank=False, unique=True)
address = models.CharField(max_length =250)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$',
message="Phone number must be entered in the format: '+999999999'. Up to 10 digits allowed.")
contact = models.CharField(max_length=100, blank=False)
phone = models.CharField(validators=[phone_regex], max_length=10, blank=True) # validators should be a list
slug = models.SlugField(unique=False)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Centre, self).save(*args, **kwargs)
class Cabin(models.Model):
random_string = str(random.randint(100000, 999999))
centre_name = models.ForeignKey(Centre, on_delete=models.CASCADE,blank=True,null=True)
code = models.CharField(max_length=6, blank=False, unique=True, default=random_string)
total_seats = models.IntegerField(blank='False')
category=models.CharField(max_length=100, default=False)
booked_date=models.DateField(blank='False')
released_date=models.DateField(blank='False')
price=models.IntegerField(blank=False, default=None)
slug = models.SlugField(unique=False,default=None)
def save(self, *args, **kwargs):
self.slug = slugify(self.category)
super(Cabin, self).save(*args, **kwargs)
In views.py file
class CabinCreateView(CreateView):
fields = '__all__'
model = Cabin
success_url = reverse_lazy("NewApp:logindex")
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.cabin = Cabin.objects.filter(slug=self.kwargs['slug'])[0]
self.object.save()
return HttpResponseRedirect(self.get_success_url())
In my cabin template,
<div class="row">
<div class="col-md-6">
<form method="POST">
{% csrf_token %}
{{ form.non_field_errors }}
<div class="col col-md-12">
<div class="fieldWrapper" >
{{ form.centre_name.errors }}
<div class="form-group col col-md-3">
<label>Centre Name</label>
{{form.centre_name}}
</div>
<div class="form-group col col-md-3" style="float:right; margin-top=-80px;width=200%">
<label for="{{form.code.id_for_label}" style="margin-left:200px;width:200%;white-space:nowrap;">Code</label>
<input type="text" placeholder="Code" value="{{form.code.value}}" name="code" maxlength="6" id="id_code" style="width:500px; margin-left:200px;">
</div>
</div>
<div class="col col-md-12">
<div class="form-group col col-md-3" style="float:right; margin-top=-80px;">
<label for="{{form.total_seats.id_for_label}" style="margin-left:200px;width:200px;white-space:nowrap;">seats</label>
<input type="text" placeholder="seats" name="total_seats" id="id_total_seats" style="width:500px; margin-left:200px;">
</div>
<div class="fieldWrapper" >
{{ form.category.errors }}
<div class="form-group col col-md-3" >
<label for="{{form.category.id_for_label}" style="margin-top:-40px">Category</label>
<input type="text" name="category" maxlength="100" id="id_category" placeholder="Category" style="width:500px";>
</div></div></div>
<div class="col col-md-12">
<div class="fieldWrapper" >
{{ form.released_date.errors }}
<div class="form-group col col-md-3" style="float:right; margin-top=-80px;">
<label for="{{form.released_date.id_for_label}" style="margin-left:200px;width:200px;white-space:nowrap;">released date</label>
<input type="text" placeholder="%yyyy-mm-dd" name="released_date" id="id_released_date" style="width:500px; margin-left:200px;">
</div>
</div>
<div class="fieldWrapper" >
{{ form.booked_date.errors }}
<div class="form-group col col-md-3" >
<label for="{{form.booked_date.id_for_label}" style="margin-top:-40px">booked date</label>
<input type="text" name="booked_date" id="id_booked_date" placeholder="%yyyy-mm-dd" style="width:500px";>
</div>
</div>
</div>
<div class="col col-md-12">
<div class="form-group col col-md-3" >
<label for="{{form.price.id_for_label}" style="margin-top:-40px">price</label>
<input type="text" name="price" maxlength="10" id="id_price" placeholder="in rupees" style="width:500px";>
</div>
</div>
<div class="form-group col col-md-3" >
<input type="submit" onclick="comparedate()" value="Save" class="btn btn-primary" style=" height:30px;width:80px;padding-bottom:2em;"/>
</div></div>
</form>
</div></div></div></div></div></div></div></div></div></div>
It looks like your problem is in your views.py file. You are telling the view that it should build a form including __all__ of the fields on the model, but in your template you don't include the slug field. Since you have overridden the save method on the Cabin model to populate the slug field, I guess you don't want that displayed in the template.
You have two options for fixing this. You could add a hidden field to the template containing the slug field. Or, what I think is a better option, you can change the fields attribute on your view to exclude the slug field.
class CabinCreateView(CreateView):
fields = ('centre_name', 'code', 'total_seats', 'category', 'booked_date', 'released_date', 'price',)
model = Cabin
success_url = reverse_lazy("NewApp:logindex")
P.S. This isn't a problem you are asking about, but I couldn't help noticing that you have what looks like multiple broken variables in your template. You might want to check if your label for="" attributes are working as expected.

Populating a drop-down from Database in Django

Hi so basically I am trying to populate a drop-down menu from a database I currently have. This will enable a 'Teacher' to select a 'Student' they input grades for. I am struggling to understand any of the tutorials online due to most of them being for django 1.9 whereas I am using 2.1.
Please if you could help, it would be much appreciated.
Template:
<form action="{% url 'subject1_view' %}" method="post">
{% csrf_token %}
<div class="form-group row">
<label class="col-2 col-form-label" for="information">Student Name</label>
<div class="col-10">
<form method="POST">
<select class="form-control" name="student_name" id="information">
{% for ?? %}
<option value="{{ ?? }}">{{ ?? }}</option>
{% endfor %}
</select>
</form>
</div>
</div>
URLs:
from django.urls import path, include
from . import views
urlpatterns = [
path('teacher_login', views.teacher_login, name='teacher_login'),
path('t_dashboard', views.t_dashboard, name='t_dashboard'),
path('subject1_view', views.subject1_view, name='subject1_view'),
path('loguserout', views.loguserout, name='loguserout'),
]
Views:
def subject1_view(request):
if request.method == 'POST':
student_name = (request.POST.get('student_name'))
current_grade = (request.POST.get('current_grade'))
previous_grade = (request.POST.get('previous_grade'))
target_grade = (request.POST.get('target_grade'))
incomplete_homework = (request.POST.get('incomplete_homework'))
behaviour_rank = (request.POST.get('behaviour_rank'))
i = Subject1(student_name=student_name, current_grade=current_grade, previous_grade=previous_grade,
target_grade=target_grade, incomplete_homework=incomplete_homework, behaviour_rank=behaviour_rank)
i.save()
return render(request, 'Teacher/dashboard.html')
else:
return render(request, 'Teacher/subject1.html')
Models:
from django.db import models
from Student.models import Student
# Create your models here.
class Subject1(models.Model):
student_name = models.ForeignKey(Student, on_delete=models.CASCADE)
current_grade = models.CharField(max_length=2)
previous_grade = models.CharField(max_length=2)
target_grade = models.CharField(max_length=2)
incomplete_homework = models.CharField(max_length=2)
behaviour_rank = models.CharField(max_length=2)
def __str__(self):
return self.student_name
Use a Django Form, and populate the choices on the form's init method:
class SelectForm(forms.Form):
student = forms.ChoiceField(widget=forms.Select(attrs={'class': 'form-control'}))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['student'].choices= [(student.id, student.name) for student in Student.objects.all()]
Template:
<form action="{% url 'subject1_view' %}" method="post">
{% csrf_token %}
<div class="form-group row">
<label class="col-2 col-form-label" for="information">Student Name</label>
<div class="col-10">
{{ form }}
</div>
</div>
</form>
Also add add form to context from View.

Django - Data in form is clearing out when sent to database

I have created a view that should update some data about a user in the database. Earlier I had issues with populating the page, but with 'user7485741' I played around with the data until I was able to populate the form how I wanted to. However, when I hit submit the database is wiped clean of data. I mean if I have a price of '$2.00' that I set in the database and then in the html page I change it to '$3.00', the database shows $0.00 in the backend. Indeed, I see that print(self.cleaned_data) returns {'user': <User: a.a#a.com>}. It isn't capturing any other fields.
Thanks!
Models.py
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
price = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
travel_flag = models.BooleanField(blank=True, default = False)
forms.py
class Meta:
model = Person
fields = ['user', 'travel_flag','price']
views.py
class UserProfileUpdateView(LoginRequiredMixin, UpdateView):
form_class = UserProfileChangeForm
template_name = 'accounts/profile-update-view.html'
def get_object(self):
qs = Person.objects.filter(pk=self.request.user.person.user_id).first()
return qs
def get_context_data(self, *args, **kwargs):
context = super(UserProfileUpdateView, self).get_context_data(*args, **kwargs)
context['title'] = 'Update Your Profile'
return context
def get_success_url(self):
return reverse("account:home")
html page
<form method='POST' action='{% if action_url %}{{ action_url }}{% endif %}'> {% csrf_token %}
<div class="form-group">
<label for="id_price" class="active">Price: $</label>
<input id="id_price" type="number" name="currency" min="0" max="99999" step="0.01" size=4 value="{{ user.person.price }}">
</div>
<div class="form-check">
<input id="id_travel" type="checkbox" name="travel" value="yes" {% if user.person.travel_flag %}checked{% endif %}>
<label for="id_travel" class="active"></label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
I figured it out. Turns out if your cleaned_form isn't passing back data, it could be because the form is not setting that field. In my case above I have
<input id="id_price" type="number" name="currency" min="0" max="99999" step="0.01" size=4 value="{{ user.person.price }}">
when I should have
<input id="id_price" type="number" name="price" min="0" max="99999" step="0.01" size=4 value="{{ user.person.price }}">
</div>
The name on an html page corresponds to the field in the database that you are updating.