I am creating a form with all sorts of headers and question before each field.
I initially create a form in Forms.py, but it seems quite difficult to customise it the way I want it.
I resorted to build the form in the html template.
I am struggling to create a drop down menu.
In this example, when selecting the colour, users would have 4 choices listed in the choices in models.py. I am clearly not linking the form in the template to the actual form.
What am I doing wrong?
(I should say, I saw a couple of things on here about fieldsets, but I dont understand the actual solutions explained)
Here is the code
Models.py
LOOKCOLOUR=(
(1,'Straw'),
(2,'Yellow'),
(3,'Gold'),
(4,'Amber'),
)
class ChampageRating(models.Model):
user = models.ForeignKey(User,blank=True, on_delete=models.CASCADE)
product=models.ForeignKey(Product,related_name="champcomments", on_delete=models.CASCADE)
look_colour=models.IntegerField(choices=LOOKCOLOUR, default=0)
..
def __str__(self):
return '%s - %s - %s'%(self.user, self.product, self.date_added)
forms.py
class ElderFlowerChampForm(ModelForm):
class Meta:
model = ChampageRating
fields = ('look_colour', )
labels ={
'look_colour': 'Colour',
}
widgets = {
'look_colour':forms.Select(attrs={'class':'form-control'}),
}
views.py
def elderflowerchamp(request, product_id):
global ChampageRating
product = Product.objects.get(pk=product_id)
url = request.META.get('HTTP_REFERER')
submitted = False
try:
if request.method == "POST":
reviews = ChampageRating.objects.get(pk=product_id)
if request.user.is_superuser:
form = ElderFlowerChampFormAdmin(request.POST, instance=reviews)
if form.is_valid():
form.save()
return redirect('home')
else:
form = ElderFlowerChampForm(request.POST, instance=reviews)
if form.is_valid():
ChampageRating = form.save(commit=False)
ChampageRating.user = request.user
ChampageRating.save()
return redirect('home')
else:
#This part goes to the page, but doesnt submit
reviews = ChampageRating.objects.get(pk=product_id)
if request.user.is_superuser:
form = ElderFlowerChampFormAdmin
else:
form = ElderFlowerChampForm
if 'submitted' in request.GET:
submitted = True
except:
reviews = None
if request.user.is_superuser:
form = ElderFlowerChampFormAdmin(request.POST)
if form.is_valid():
data = ChampageRating()
data.rating = form.cleaned_data['rating']
data.look_colour = form.cleaned_data['look_colour']
data.ip = request.META.get('REMOTE_ADDR')
data.product_id = product_id
data.user_id = request.user.id
data.save()
messages.success(request, 'Thank you! Your review has been submitted.')
return redirect('home')
else:
form = ElderFlowerChampForm(request.POST)
if form.is_valid():
data = ChampageRating()
data.rating = form.cleaned_data['rating']
data.look_colour = form.cleaned_data['look_colour']
data.ip = request.META.get('REMOTE_ADDR')
data.product_id = product_id
data.user_id = request.user.id
data.save()
messages.success(request, 'Thank you! Your review has been submitted.')
return redirect('home')
template
<form action="{% url 'ElderFlowerReview' product.id%}" method="POST">
{%csrf_token%}
<h4>Look</h4>
<h5>Colour</h5>
<select name="look_colour" rows="4" class="form-control"></select>
</form>
Just in case someone comes across the same problem, I kinda found the solution. (it creates new problems, but it's a start).
Source here: https://github.com/hadpro24/django-forms-fieldset
Here is what I did.
I installed fieldsets (thats the part I was missing on other posts)
pip install django-forms-fieldset
I added fieldsets in the installed apps (in setting.py)
INSTALLED_APPS = [
,
'forms_fieldset']
in forms.py Fieldsets need to be added before Meta
class ElderFlowerChampFormAdmin(ModelForm):
fieldsets=[
("Look",{'fields':[
('look_colour','look_clarity','look_effervescence')]}),
]
class Meta:
model = ChampageRating
fields = ('user','look_colour', ..)
labels ={
'user': '',
'product': '',
'look_colour': '',
}
widgets = {
'user': forms.Select(attrs={'class':'form-control'}),
'look_colour':forms.Select(attrs={'class':'form-control'}),
}
then in the template
{% load forms_fieldset static %}
<link rel="stylesheet" type="text/css" href="{% static 'forms_fieldset/css/main.css' %}">
<form action="{% url '#' product.id%}"method = POST>
{%csrf_token%}
{{ form|fieldset}}
<button class="btn btn-secondary">Add Review</button>
</form>
Related
I made a feedback form (def feedBack) so that a user can give feedback. It's working well. Now my motive to create an update form so that a user can be able update their feedback. I also have written a view for update feedback (def UpdateFeedback). But it's not working perfectly. When I submit the update form, then it updates none. Where did the actual problem occur?
views.py:
This view for storing feedback and it's working well.
def feedBack(request,quick_view_id):
quick_view = get_object_or_404(Products, pk=quick_view_id)
if request.method == "POST" and request.user.is_authenticated:
try:
ProductREVIEWS.objects.create(
user=request.user,
product=quick_view,
feedBACK=request.POST.get('feedBACK')
)
return redirect('quick_view', quick_view_id)
except:
return redirect('quick_view', quick_view_id)
else:
return redirect('quick_view', quick_view_id)
this view for update the feedback, but it's store none
def UpdateFeedback(request, id):
feedback = ProductREVIEWS.objects.get(pk=id)
product_id = feedback.product.id
reviewers = feedback.user
if request.method == "POST":
form = UpdateFeedbackForm(request.POST)
if form.is_valid() and reviewers.id == request.user.id:
UpdateFeedbackForm(request.POST)
feedBACK = form.cleaned_data.get('UpdateFeedBACK')
feedback.feedBACK = feedBACK
feedback.save()
messages.success(request, "Feedback is updated")
return redirect('quick_view', product_id)
forms.py:
class UpdateFeedbackForm(forms.ModelForm):
class Meta:
model = ProductREVIEWS
fields = ('feedBACK')
labels = {
'feedBACK':'Change Your View'
}
widgets = {
'feedBACK':forms.Textarea(attrs={'class':'form-control', 'style':'font-size:13px;'})
}
models.py:
class ProductREVIEWS(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='userREVIEW',on_delete=models.CASCADE)
product = models.ForeignKey(Products, related_name='productREVIEWrelatedNAME',on_delete=models.CASCADE)
feedBACK = models.TextField(blank=True, null=True)
urls.py:
path("feedBack/<int:quick_view_id>/", views.feedBack, name="feedBack"),
path("UpdateFeedback/<int:id>/", views.UpdateFeedback, name="UpdateFeedback")
template:
{% for feedBack in AllProductFeedback %}
<form action="{% url 'UpdateFeedback' id=feedBack.id %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<textarea id="UpdateFeedBack" rows="6" style="font-size: 13px;" class="form-control" name="UpdateFeedBACK" value="" required>{{feedBack.feedBACK}}</textarea>
</form>
{% endfor %}
def UpdateFeedback(request, id):
feedback = ProductREVIEWS.objects.get(pk=id) # I suggest you to handle DoesNotExist exception case.
product_id = feedback.product.id
reviewers = feedback.user
if request.method == "POST":
form = UpdateFeedbackForm(request.POST, instance=feedback) # you need to pass instance here
if form.is_valid() and reviewers.id == request.user.id:
UpdateFeedbackForm(request.POST) # doesn't do anything here, return value ignored.
feedBACK = form.cleaned_data.get('UpdateFeedBACK') # Will return None, since there is no UpdateFeedBACK in form fields.
# So you can remove two lines above.
form.save() # This will update the ProductREVIEWS model
messages.success(request, "Feedback is updated")
return redirect('quick_view', product_id)
def UpdateFeedback(request, id):
feedback = ProductREVIEWS.objects.get(pk=id)
product_id = feedback.product.id
reviewers = feedback.user
if request.method == "POST":
form = UpdateFeedbackForm(request.POST)
if form.is_valid() and reviewers.id == request.user.id:
UpdateFeedbackForm(request.POST)
feedBACK = form.cleaned_data.get('UpdateFeedBACK') #this will not work since there is no updatefeedback field in your form
feedback=form.save(commit=False)
feedback.feedBACK = feedBACK #(this feedBACK need to be object of your Products model)
feedback.save()
messages.success(request, "Feedback is updated")
return redirect('quick_view', product_id)
this might be a pretty stupid question. Also I am new to django. But I was trying to create a basic file upload approach with django where user uploads a file and it gets stored into the defined media path (or whatever that it's called) and that the file size, name, and some other attributes that are needed can be stored into the database. So I have the model ready which will help you understand the question better.
class Document(models.Model):
file_uid = models.CharField(max_length = 16)
file_name = models.CharField(max_length = 255)
file_size = models.CharField(max_length = 255)
file_document = models.FileField(upload_to='uploaded_files/')
uploaded_on = models.DateTimeField(auto_now_add=True)
uploaded_by = models.CharField(max_length=16)
Now it's clearly plain that we don't need to create all the fields in the form and that most them can be received from the file itself (like the name, size). for other attrs like uid and uploaded by those also will be added by the backend. So that's where I am stuck. I have searched for 2 days straight and still couldn't find a proper solution.
As of now this is my views.py
def uploadView(request):
if(request.method == 'POST'):
form = FileUploadForm(request.POST, request.FILES)
uploaded_file = request.FILES['uploaded_file']
file_dict = {
'file_uid' : get_random_string(length=10),
'file_name' :uploaded_file.name,
'file_size' : uploaded_file.size,
'file_document' : request.FILES['uploaded_file'],
'uploaded_by' : get_random_string(length=10)
}
form = FileUploadForm(data=file_dict)
if form.is_valid():
form.save()
return HttpResponse("You reached here")
else:
return HttpResponse("Your form is invalid")
else:
form = FileUploadForm(request.POST, request.FILES)
return render(request, 'function/upload.html', {
'form':form
})
I don't know if this is correct but as of know the form.isvalid() is false.
here's my forms.py
class FileUploadForm(forms.ModelForm):
file_document = forms.FileField(widget=forms.FileInput(attrs={'name':'uploaded_file'}))
class Meta:
model = Document
fields = ('file_uid', 'file_name', 'file_size', 'file_document', 'uploaded_by')
and my upload page section looks like this
<body>
<h1>Upload a file</h1>
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="uploaded_file">
<button type="submit">Upload</button>
</form>
</body>
If you can mercifully guide me into a proper way of doing this i'll be really gratefull.
Before solution, Here are few issues i found in your code
Major issue is how you tried to update the name of your file_document input, it doesn't work this way. confirm this by inspecting in devtools.
Checkout my answer here to update name attribute of django input form field.
Without updating this, you are not getting file from form.
Not issues just something i would like to point out
def uploadView(request):
if(request.method == 'POST'):
form = FileUploadForm(request.POST, request.FILES)
# your code in between, here the above form is never used and the overridden by the form in next line so why assigning it
form = FileUploadForm(data=file_dict)
# your form.is_valid() code start here
else:
form = FileUploadForm(request.POST, request.FILES)
# This block will only run for GET request, why using request.POST, request.FILES
return render(request, 'function/upload.html', {
'form':form
})
Here is how i got your code working
update FileUploadForm like this
class FileUploadForm(forms.ModelForm):
class Meta:
model = Document
fields = ('file_uid', 'file_name', 'file_size', 'file_document', 'uploaded_by')
# below code is only used to change the name of file_document to uploaded_file
custom_names = {'file_document': 'uploaded_file'}
def add_prefix(self, field_name):
field_name = self.custom_names.get(field_name, field_name)
return super(FileUploadForm, self).add_prefix(field_name)
use form in html like this
<form method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
{{form.file_document}}
<input type="submit" value="send"/>
</form>
Update view as
def uploadView(request):
if(request.method == 'POST'):
uploaded_file = request.FILES['uploaded_file']
file_dict = {
'file_uid' : 'test1',
'file_name' :uploaded_file.name,
'file_size' : uploaded_file.size,
'uploaded_by' : 'hemant'
}
form = FileUploadForm(file_dict, request.FILES)
if form.is_valid():
form.save()
return HttpResponse("You reached here")
else:
return HttpResponse("Your form is invalid")
else:
form = FileUploadForm()
return render(request, 'function/upload.html', {
'form':form
})
Currently both of these print statements either log 'None' or just do not show at all. Even if I just print("hello') in the form.is_valid conditional I do not see it. Not sure what I have done wrong but the data was saving into the django admin but now it is not...
def create_user_account(request, *args, **kwargs):
form = UserAccountForm(request.POST or None)
print(request.POST.get('account_email'))
if form.is_valid():
print(form['account_email'].value())
form.save()
form = UserAccountForm()
context = {
'form': form
}
return render(request, 'registration/register_user.html', context)
and the html:
{% block content %}
<form action="{% url 'home' %}" method='POST'> {% csrf_token %}
{{form.as_p}}
<input type='submit' value='Submit'/>
</form>
{% endblock %}}
edit: i do get "POST / HTTP/1.1" 200 when I send the request which is weird. I don't see a 201 or 302 like I would expect.
forms.py:
class UserAccountForm(forms.ModelForm):
class Meta:
model = UserAccount
fields = [
'first_name',
'last_name',
'account_email',
'country'
]
class UserAccount(models.Model):
first_name = models.CharField(max_length=30, null=False)
last_name = models.CharField(max_length=30, null=False)
account_email = models.EmailField(max_length = 254, null=False)
country = models.CharField(choices=COUNTRY_CHOICES, null=False,
max_length=75)
activated = models.BooleanField(default=False)
CONTINUED:
So I dropped the database, migrated and the table appeared like normal. I tried to save data and the same problem? BUT I also tried to add information manually through the admin site and got this error:
OperationalError at /admin/profiles/useraccount/add/
no such table: main.auth_user__old
So I updated to Django 3.0.6 and this solved the problem - can now add manually but still cannot add through the form.
This was the solution. I don't know why it stopped working but it did. I feel I got finessed by indentation.
def register(request):
if request.method == 'POST':
form = UserAccountForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
form = UserAccountForm()
context = {
'form': form
}
return render(request, 'registration/register_user.html', context)
I'm trying to get a photo to upload and the form is not seeing the file and in the form.errors, it says 'this field is required'. I've tried using picture = request.FILES['picture'] to no avail and have also tried picture = form.FILES['picture'] as well as picture = request.POST.FILES['picture'] and picture = form.cleaned_data.get('picture') What am I missing? Let me know if you need anymore information
template
{% block content %}
<h1>Create {{post_type.title}} Post</h1>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{form.as_p}}
<button type='submit'>Submit</button>
</form>
{% endblock %}
forms.py
class PicturePostForm(forms.ModelForm):
class Meta:
model = PicturePost
fields = ('description', 'privacy', 'picture', 'categories')
views.py
#login_required()
def picture_post(request):
"""
Creates new picture post
"""
if request.method == "POST":
form = PicturePostForm(request.POST)
print("is post")
if form.is_valid():
print("is valid") # this never gets printed because of the 'this field is required' error
author = request.user
content = form.cleaned_data['description']
category = form.cleaned_data['categories']
picture = form.cleaned_data['picture']
privacy = form.cleaned_data['privacy']
p_post = PicturePost(author=author, description=content, categories=category, picture=picture,privacy=privacy )
p_post.save()
#redirect to last page
return redirect('home')
else:
l = []
for i in form.errors.keys():
l.append(form.errors[i])
return HttpResponse(l)
else:
post_type = 'picture'
form = PicturePostForm()
return render(request, 'create_post.html', {'form': form, 'post_type': post_type})
The corresponding model field
picture = models.ImageField(upload_to=f'profiles/{User}_gallery', max_length=255)
Fixed it by replacing form = PicturePostForm(request.POST) with form = PicturePostForm(request.POST, request.FILES)
I have tried to complete the code before, please following
views
#login_required()
def picture_post(request):
"""
Creates new picture post
"""
form = PicturePostForm(request.POST or None, request.FILES or None)
if request.method == "POST":
if form.is_valid():
# instance new object p_post (this best practice if using forms.ModelForm)
# commit=False (to save data on ram/memory device without database/hardrive)
p_post = form.save(commit=False)
# assign author attribute from thr current user session
p_post.author = request.user
# commit=True to move/save data from memory to harddrive
p_post.save() # p_post.save(commit=True)
return redirect('home')
else:
l = []
for i in form.errors.keys():
l.append(form.errors[i])
return HttpResponse(l)
post_type = 'picture'
return render(request, 'create_post.html', {'form': form, 'post_type': post_type})
I have two forms:
class Form_registration_security (ModelForm):
class Meta:
model = Security
fields = ['fk_id_users_security', 'e_mail', 'password']
widgets = {
'e_mail': forms.TextInput(attrs = {'placeholder': 'Your Email'}),
'password': forms.TextInput(attrs = {'placeholder': 'New Password'}),
}
class Form_registration_user (ModelForm):
class Meta:
model = Users
fields = ['id', 'first_name', 'last_name', 'date_birthdaty']
widgets = {
'id': forms.TextInput(attrs = {'placeholder': 'id'}),
'first_name': forms.TextInput(attrs = {'placeholder': 'First Name'}),
'last_name': forms.TextInput(attrs = {'placeholder': 'Last Name'}),
'date_birthdaty': forms.TextInput(attrs = {'placeholder': 'Date'})
}
But data saves only in one mode - (Form_registration_user).
Code in view:
def save_registration (request ):
if request.method == 'POST':
form_user = Form_registration_user(request.POST)
form_security = Form_registration_security(request.POST)
if form_user.is_valid() and form_security.is_valid():
data_user = form_user.save()
data_security = form_security.save(commit=False)
data_security.data_user = data_user
data_security.save()
return render_to_response('see_you_later.html')
else:
return render_to_response('error.html')
I'm always see - error.html, although I'm fill right form.
Model User have a primary key.
Model Security have a foreign key.
My template:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div class="entry_or_register">
{% load staticfiles %}
<img src="{% static "tumblr.gif" %}" width="1250" height="550">
<form name="registration" method="post" action="save/">
{% csrf_token %}
{{ form_registration_user.as_p }}
{{ form_registration_security.as_p }}
<input type="submit" value="SignUp">
</form>
</div>
</body>
</html>
Thanks for help! Right function:
def save_registration (request ):
if request.method == 'POST':
form_user = Form_registration_user(request.POST)
form_security = Form_registration_security(request.POST, request.FILES)
if form_user.is_valid():
data_user = form_user.save()
data_security = form_security.save(commit=False)
data_security.data_user = data_user
data_security.save()
return render_to_response('see_you_later.html')
else:
return render_to_response('error.html')
You should post also the html markup of the corresponding template.
Anyway, here's a view i used once i had to save data from two ModelForms in the same page, with the user clicking a single submit button:
def register(request):
message = None
if request.method == 'POST':
user_form = NewUserForm(request.POST)
details_form = UserDetailsForm(request.POST, request.FILES)
if user_form.is_valid():
new_simple_user = user_form.save()
message = _("User inserted")
if details_form.is_valid():
# Create, but don't save the new user details instance.
new_user_details = details_form.save(commit=False)
# Associate the user to the user details
new_user_details.user = new_simple_user
# save a new user details instance
new_user_details.save()
message = _("User details inserted")
else:
user_form = NewUserForm()
details_form = UserDetailsForm()
return render_to_response('register.html', { 'user_form': user_form, 'details_form': details_form, 'message': message,},\
context_instance=RequestContext(request))
I'm not sure how you rendered your forms in the template, but it could be that when you click submit, only one of the forms sends its data in the HTTP request.
Then the other form's constructor won't find its key in the POST variable and the outcome won't be a valid form. I think that's why you test always fail.
Now, I hope you could give us some more details on what you're trying to do but I think you are going to need a custom Form class (that would be the union of your two current forms) instead of a ModelForm.
EDIT : sorry, you shouldn't actually need to do that...
Good luck.