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.
Related
Django form is saved but "result" field is showing empty in database.
Even after populating the filed from admin panel, it is saved but it still shows empty.
Models.py
class Result(models.Model):
class Choises(models.TextChoices):
POSITIVE = "POSITIVE", "Positive"
NEGATIVE = "NEGATIVE", "Negative"
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50, default=None)
result = models.CharField(max_length = 100, choices=Choises.choices, blank=False
)
resultDoc = models.ImageField(upload_to='testResults', height_field=None, width_field=None,)
def __str__(self):
return self.user.username
Forms.py
class resultForm(forms.ModelForm):
class Meta:
model = Result
fields = ['name', 'result', 'resultDoc']
views.py
def inputResult(request, pk):
user = User.objects.filter(id=pk).first()
profile = newProfile.objects.filter(user=user).first()
if profile == None:
profile = oldProfile.objects.filter(user=user).first()
rForm = resultForm(request.POST, request.FILES)
if request.method == 'POST':
rForm = resultForm(request.POST, request.FILES)
if rForm.is_valid():
order = rForm.save(commit=False)
order.user_id = pk
order.save()
return redirect('stored_records')
else:
rForm = resultForm()
context = {'user' : user, 'profile':profile, 'rForm': rForm}
return render(request, 'Testing booth End/input-result-customer-info.html', context)
input-result-customer-info.html
<form action="" method = "POST" enctype= "multipart/form-data">
{% csrf_token %}
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name" placeholder="Uploaded By/Doctor Name">
</div>
<div class="mb-3">
<label for="result" class="form-label">Result</label>
<select class="form-select" id="result" name="result" class="form-control">
<option value="POSITIVE">Positive</option>
<option value="NEGATIVE">Negative</option>
</select>
</div>
<div class="mb-3">
<label>Upload Scan File</label>
<div class="upload d-flex justify-content-between">
<div class="file-placeholder">Upload Scan File</div>
<input type="file" class="form-control d-none" id="resultDoc" name="resultDoc" >
<label for="resultDoc" class="form-label cam-img"> <img src="{% static 'user/images/Camera.png' %}"> </label>
</div>
</div>
<button class="btn btn-primary w-50 ms-auto d-block h-100" type="submit">Upload</button>
</form>
enter image description here
I think that the reason this doesnt work is that you created a form (rForm) in the backend but then you don't use it in the frontend.
This is how you should render your form in the the frontend:
<form method="post">
{{ rForm.as_p }} # This is the easiest possible implementation
<button type="submit">Submit</button>
</form>
If you want to take control of how the form is rendered, then you have to make sure that the input fields are named in the way that your backend expects. You can do it entirely manually or semi-manually, but your field names have to be set correctly or nothing will work.
Example of typical approach, say in case you have several similar text inputs
{% for field in rForm %}
<label for="{{ field.auto_id }}">{{ field.name }}</label>
<input type="text" name="{{ field.html_name }}" id="{{ field.auto_id }}" />
{% endfor %}
Example of fully hands-on approach
<select class="form-select" id="{{ rForm.result.auto_id }}" name="{{ rForm.result.html_name }}" class="form-control">
<option value="POSITIVE">Positive</option>
<option value="NEGATIVE">Negative</option>
</select>
In order to make sure that the inputs are being parsed correctly, add a print statement in your view to print the POST request:
print("Post Request : ", request.POST)
From there you will be able to see if the result field is being picked up correctly or if it's being ignored. Usually when fields get ignored is because they are not named correctly or sometimes it's because they fail validation.
If the rest of the data is saved correctly and just result is being left out then it's almost for sure an issue with the field name because if the form failed validation it would have aborted the entire operation.
P.S. I just noticed that you select input has the class attribute declared twice
I want to save a html form data into django model but it has a foreign key field from another model. How can I save a form which has FK field??
My models:
class Dish(models.Model):
title =models.CharField(max_length=200)
description =models.TextField(blank=True)
price =models.IntegerField()
photo_main= models.ImageField(upload_to="photos/%Y%m%d/")
photo_1= models.ImageField(upload_to="photos/%Y%m%d/", blank= True)
photo_2= models.ImageField(upload_to="photos/%Y%m%d/", blank= True)
def __str__(self):
return self.title
class Order(models.Model):
dishorder= models.ForeignKey(Dish,null=True,on_delete=models.CASCADE)
name = models.CharField(max_length=200,blank=True)
email = models.CharField(max_length=100,blank=True)
phone = models.CharField(max_length=100,blank=True)
quantity =models.IntegerField(blank=True)
def __str__(self):
return self.name
My views:
def order(request):
if request.method == 'POST':
name = request.POST['name']
email = request.POST['email']
phone = request.POST['phone']
quantity = request.POST['quantity']
order= Order(
name=name,
email=email,
phone=phone,
quantity=quantity)
order.save()
messages.success(request, "Your order has been submitted.")
return render(request,"dishes/order.html")
My urls:
urlpatterns = [
path("dish",views.dish,name="dish"),
path("dish/<pk>",views.dishsingle,name="dishsingle"),
path("order",views.order,name="order"),
]
My template dishes/order.html
<form method="POST">
{% csrf_token %}
<div>
<label for="name">Name:</label>
<input type="text" name="name" class="form-control" required>
</div>
<div>
<label for="email">Email:</label>
<input type="email" name="email" class="form-control" required>
</div>
<div>
<label for="phone">Phone:</label>
<input type="number" name="phone" class="form-control" required>
</div>
<div>
<label for="quantity">Quantity:</label>
<input type="number" name="quantity" class="form-control" required>
</div>
<hr>
<input type="submit" value="MAKE AN ORDER">
</form>
While submitting this html form, I would like the foreignkey field dishorder to be saved on the backend as well. When I check the admin page, order is saved but without the name of the dish. How can I resolve this?
One of my models is a Product model which has 3 different price fields, so that one of its prices can be chosen when creating an order. I created a select input in one of my forms to allow the user to choose one of the prices, but when the form is submitted, Django returns an error telling me to introduce a valid number, even though I checked the request.POST and the value it's sending is a valid number.
models.py:
class Product(models.Model):
...
price_1 = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=0) # Separador de miles
price_2 = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=0) # Separador de miles
price_3 = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=0) # Separador de miles
forms.py:
class CartAddProductForm(forms.Form):
price = forms.DecimalField()
quantity = forms.IntegerField(
label ='Amount'
)
override = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput)
views.py:
#require_POST
def cart_add(request, product_id):
cart = Cart(request)
product = get_object_or_404(Product, id=product_id)
form = CartAddProductForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
cart.add(product=product, quantity=cd['quantity'], price=cd['price'], override_quantity=cd['override'])
return redirect('cart:cart_detail')
Template:
<form action="{% url 'cart:cart_add' product.id %}" method="post">
{% csrf_token %}
<div class="form-group">
<label for="id_price">Precio</label>
<select name="price" id="id_price" class="select2 form-control" data-toggle="select2" required>
<option value="{{ product.price_1 }}" selected>Precio retail: ${{ product.price_1|intcomma }}</option>
{% if product.price_2 %}
<option value="{{ product.price_2 }}">Precio mayorista: ${{ product.price_2|intcomma }}</option>
{% endif %}
{% if product.price_3 %}
<option value="{{ product.price_3 }}">Otro precio: ${{ product.price_3|intcomma }}</option>
{% endif %}
</select>
</div>
<div class="form-group">
<label for="id_quantity"></label>
<input data-toggle="touchspin" type="number" value="1" data-bts-max="100000" required id="id_quantity" name="quantity" data-bts-button-down-class="btn btn-danger" data-bts-button-up-class="btn btn-primary">
<input type="hidden" name="override" value="False" id="id_override">
</div>
<button type="submit" class="btn btn-secondary">
<i class="uil-plus-circle"></i>
Agregar a la orden
</button>
</form>
When debugging the view, form.is_valid() returns False. When checking form.errors, it effectively shows {'price': ['Enter a valid number.']}, but when checking request.POST, it shows 'price': ['40000,00'], 'quantity': ['1'], 'override': ['False'], so the number is valid, but for some reason it is not passing to the is_valid() method as a valid number. Why would that be?
Could this be a localization issue?
price = forms.DecimalField(decimal_places=2, localize=True)
The comma as decimal might be causing the form to fail.
'''Models Code'''
# Product Model
class Products(models.Model):
name = models.CharField(max_length=50)
img = models.ImageField(upload_to='productImage')
CATEGORY = (
('Snacks','Snacks'),
('Juice','Juice'),
)
category = models.CharField(max_length=50, choices=CATEGORY)
description = models.TextField()
price = models.FloatField()
# Rating Model
class Rating(models.Model):
product = models.ForeignKey(Products, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
stars = models.IntegerField(validators=[MinValueValidator(1),MaxValueValidator(5)], blank=True, null=True)
comment = models.TextField(blank=True,null=True)
''' Views Code '''
class ProductListView(ListView):
model = Products
template_name = 'products.html'
context_object_name ='Products'
class ProductDetailView(LoginRequiredMixin,DetailView):
login_url = '/accounts/login'
model = Products
# Using this function I want to take the rating and comment, but how can I access the cuurent object for which the comment and rating is being send by the user.
def review(request,slug):
star=request.POST.get('rating')
comment=request.POST.get('comment')
user = request.user
productId = request.POST.get('productsid') # How to get the Product
product = Products.objects.get(id=productId)
review = Rating(product=product,user=user,stars=star,comment=comment)
review.save()
return redirect('/')
# Urls code
urlpatterns = [
path('',views.home,name='Home'),
path('products',ProductListView.as_view(),name='Products'),
path('product/<int:pk>',ProductDetailView.as_view(),name='Product-Details'),
path('contact',views.contact,name='Contact'),
path('review',views.review,name='review')
#Templates Code
<form method="POST" action="review">
{% csrf_token %}
<input type="hidden" id="rating-value" name="rating">
<textarea style="margin-top:5px;" class="form-control" rows="3" id="comment" placeholder="Enter your review" name="comment"></textarea>
<button type="submit" style="margin-top:10px;margin-left:5px;" class="btn btn-lg btn-success">Submit</button>
</form>
How to fetch the current object from the deatailed view page in the review function?
I have added the code here. In Product detailed view page it is rendering the page through which I want to take rating and comment for the product . Is there any other way through which I can get the product, user , star, and rating field value and store it in the data base?
I can point out some ways to retrieve the product_id in your review function.
First approach:
You can pass the product_id as a URL parameter. In this case, I hope the review view is called from the product detail page.
So, your url should be something like:
path('review/<int:product_id>', views.review, name="review),
Your view:
def review(request, *args, **kwargs):
star=request.POST.get('rating')
comment=request.POST.get('comment')
user = request.user
productId = kwargs.get('product_id') # change is here
product = Products.objects.get(id=productId)
review = Rating(product=product,user=user,stars=star,comment=comment)
review.save()
return redirect('/')
Your template:
<form method="POST" action="{% url 'review' object.pk %}">
{% csrf_token %}
<input type="hidden" id="rating-value" name="rating">
<textarea style="margin-top:5px;" class="form-control" rows="3" id="comment" placeholder="Enter your review" name="comment"></textarea>
<button type="submit" style="margin-top:10px;margin-left:5px;" class="btn btn-lg btn-success">Submit</button>
</form>
In the template, the object is the object_name you have given to the product object. You can change the object name by adding:
context_object_name = product
in your ProductDetailView.
Second approach:
Pass the product_id as a form data. You can create a hidden input in your template that will contain the product_id as value. For example:
In your template:
<form method="POST" action="review">
{% csrf_token %}
<input type="hidden" id="rating-value" name="rating">
<input type="hidden" name="product_id" value="{{ object.pk }}"> # add a hidden input field
<textarea style="margin-top:5px;" class="form-control" rows="3" id="comment" placeholder="Enter your review" name="comment"></textarea>
<button type="submit" style="margin-top:10px;margin-left:5px;" class="btn btn-lg btn-success">Submit</button>
</form>
Where object is what I mentioned previously.
Then you can retrieve the product_id in view as:
def review(request,slug):
star=request.POST.get('rating')
comment=request.POST.get('comment')
user = request.user
productId = int(request.POST.get('product_id')) # here
product = Products.objects.get(id=productId)
review = Rating(product=product,user=user,stars=star,comment=comment)
review.save()
return redirect('/')
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)