I want to create an edit page where the customer can edit the profile page. I have a problem with the validators and I don't know how to solve this.
model.py
class UserProfile(models.Model):
CAT_G = (
('W', 'W'),
('M', 'M'),
('do not want to mention', 'do not want to mention'),
)
age = models.IntegerField(default=1, validators=[ MaxValueValidator(100), MinValueValidator(1)])
height = models.DecimalField(max_digits=3, validators=[MinValueValidator(Decimal('0.0'))], decimal_places=2)
gender = models.CharField(max_length=27, blank=False, null= False, choices=CAT_G)
view.py
def edit_view(request):
context={}
if request.method == "POST":
form = ProfileUpForm(request.POST, instance=request.user.userprofile)
if form.is_valid():
form.save()
return redirect('/profPage')
else:
form = ProfileUpForm(
initial={
"age":request.user.userprofile.age,
"height":request.user.userprofile.height,
"gender":request.user.userprofile.gender,
}
)
context['profE_form']= form
return render(request, 'editPage.html', context)
forms.py
class ProfileUpForm(forms.ModelForm):
class Meta:
model= UserProfile
fields =('age', 'height', 'gender', )
def clean_age(self):
if self.is_valid():
age=self.cleaned_data['age']
return age
def clean_height(self):
if self.is_valid():
height=self.cleaned_data['height']
return height
def clean_gender(self):
if self.is_valid():
gender=self.cleaned_data['gender']
return gender
editPage.html
{% for fieldProfile in profE_form %}
<p>
{{fieldProfile.label_tag}}
{{fieldProfile}}
</p>
{% endfor %}
The problem is that in the html page, the user can choose a negative number, even if I put that validators in my model.
You need to render the errors of the fields, so:
{{ profE_form.non_field_errors }}
{% for fieldProfile in profE_form %}
<p>
{{ fieldProfile.errors }}
{{ fieldProfile.label_tag }}
{{ fieldProfile }}
</p>
{% endfor %}
You should also render the profE_form.non_field_errors. For more information, see the Rendering fields manually section of the documentation.
You should not implement the .clean_…() methods, and definitely not where you call is_valid() since Django calls these .clean_…() to check if the form is valid.
You can specify the min and/or max by specifying the widget:
from django.forms.widgets import NumberInput
class ProfileUpForm(forms.ModelForm):
class Meta:
model= UserProfile
fields = ('age', 'height', 'gender', )
widgets = {
'age': NumberInput(attrs=dict(min=1, max=100)),
'height': NumberInput(attrs=dict(min=0))
}
You can simplify the view by passing the instance to the form with:
from django.contrib.auth.decorators import login_required
#login_required
def edit_view(request):
if request.method == 'POST':
form = ProfileUpForm(request.POST, request.FILES, instance=request.user.userprofile)
if form.is_valid():
form.save()
return redirect('/profPage')
else:
form = ProfileUpForm(instance=request.user.userprofile)
context = {'profE_form': form}
return render(request, 'editPage.html', context)
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
Related
guys, I am beginner at Django. I watch lessons on youtube and get different results. I use Django==2.0.7 and Python==3.6.5.
I try to get error on my page, if I write not correct name of title, but I don't get it. Look at func - **def clean_title(self, *args, kwargs), I hope, you understand, what I mean. There I have "raise forms.ValidationError("Error")", but it doesn't work anymore.
forms.py
from django import forms
from .models import Product
class ProductForm(forms.ModelForm):
title = forms.CharField(label='',
widget=forms.TextInput(attrs={"placeholder": "title"}))
Description = forms.CharField(
required=False,
widget=forms.Textarea(
attrs={
"placeholder": "Your description",
"class": "new-class-name two",
"id": "new-class-name two",
"rows": 20,
'cols':120
}
)
)
Price = forms.DecimalField(initial=199.99)
class Meta:
model = Product
fields = [
'title',
'Description',
'Price'
]
def clean_title(self, *args, **kwargs):
title = self.cleaned_data.get('title')
if "Ruslan" in title:
return title
else:
raise forms.ValidationError("Error")
In forms.py I created class ProductForm and declared the fields. In my page I see it.
Also I defined clean_title. I wanted to get error if I filled in wrong name of title.
views.py
from django.shortcuts import render
from .models import Product
from .forms import ProductForm as CreateForm, PureDjangoForm
def create_form(request):
form = CreateForm(request.POST or None)
if form.is_valid():
form.save()
form = CreateForm()
context = {
'form': form
}
return render(request, "create_product_form.html", context)
create_product_form.html
{% extends 'base.html' %}
{% block content %}
<form action='.' method="POST"> {% csrf_token %}
{{ form.as_p }}
<input type='submit' value='Save' />
</form>
{% endblock %}
This is my file.html, it inherits any not important details from base.html.
Guys, what's wrong, help me, please, I can't understand how I can get error if name of title is not correct? I see all fields on my page and I can fill in it, but it doesn't show me errors.
You should not create a new form if you made one that is invalid, the view thus should look like:
def create_form(request):
if request.method == 'POST':
form = CreateForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('name-of-some-view')
else:
form = CreateForm()
context = {
'form': form
}
return render(request, 'create_product_form.html', context)
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.
Django 2.0
Python 3.6
I am having trouble with a Django form that is not saving the file that is selected through the form; whenever you select a file to upload, I receive the message "This Field is Required.".
I placed a blank=True and a null=True in the Model FileField to get rid of the same, but whenever I attempt to load the html, I get this error: "The 'copydoc' attirbute has no file associated with it."
I would like for a user to be able to log in, create an entry and upload a file along with said entry. Why doesn't the DB accept the file from the form?
Thank you.
views.py:
from django.shortcuts import render, redirect
from .models import notarizer, CustomUser, notarizerCreateForm
# from .forms import notarizerCreateForm
# Create your views here.
def home(request):
t = 'home.html'
return render(request, t)
def page1(request):
t = 'log1/page1.html'
if request.user.is_authenticated:
logger = notarizer.objects.filter(userziptie=request.user).order_by('-date')
return render(request, t, {'logger': logger})
else:
return redirect(home)
def create_entry(request):
createPath = 'log1/create_entry.html'
if request.method == 'POST':
if request.method == 'FILES':
form = notarizerCreateForm(request.POST, request.FILES)
if form.is_valid():
instance =notarizerCreateForm(
file_field=request.FILES['file']
)
instance.save()
else:
print(form.errors)
else:
form = notarizerCreateForm(request.POST)
if form.is_valid():
form.save()
else:
print(form.errors)
else:
form = notarizerCreateForm()
return render(request, createPath, {'form': form})
create_entry.html:
{% extends "base.html" %}
{% block placeholder1 %}
<div class="form-holder">
<form name="form" enctype="multipart/form-data" method="POST"
action="/create_entry/" >
{% csrf_token %}
{{ form.as_table }}
<input type="submit"/>
</form>
</div>
{% endblock %}
models.py:
from django.db import models
from users.models import CustomUser
from django.forms import ModelForm
# Create your models here.
class notarizer(models.Model):
date = models.DateField(auto_now_add=True)
docName = models.CharField(max_length=25, null=False)
describe = models.TextField(max_length=280)
signee = models.CharField(max_length=25, null=False)
signeeDets = models.TextField(max_length=280)
copydoc = models.FileField(upload_to='users/', blank=True, null=True)
userziptie = models.ForeignKey('users.CustomUser',
on_delete=models.DO_NOTHING, null=True)
def __str__(self):
return "{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}".format(
self.pk,
self.date,
self.docName,
self.describe,
self.signee,
self.signeeDets,
self.userziptie
)
class notarizerCreateForm(ModelForm):
class Meta:
model = notarizer
fields = ['docName','describe','signee','signeeDets', 'copydoc']
There are some things that make the view workflow very weird:
you check request.method, first you check if it is a 'POST' which is a good idea, but then you check if it is 'FILES', there is no HTTP method named FILES, there are only GET, POST, PATCH, PUT, OPTIONS, etc.;
you call form.is_valid() which is again what should happen, but then you create a new Form, and only pass it a single parameter; and
in case of a POST you should not return a rendered page, but redirect to a GET page (for example showing the result). The workflow is typically Post-redirect-get, since if the user refreshes their browser, we do not want to make the same post again.
The workflow should look like:
def create_entry(request):
createPath = 'log1/create_entry.html'
if request.method == 'POST': # good, a post (but no FILES check!)
form = notarizerCreateForm(request.POST, request.FILES)
if form.is_valid():
instance = form.save()
else:
# you probably want to show the errors in that case to the user
print(form.errors)
# redirect to a page, for example the `page1 view
return redirect(page1)
else:
form = notarizerCreateForm()
return render(request, createPath, {'form': form})
Problem description: UserProfile form doesn't save any data.
I am creating a new User and automatically create a UserProfile object for him (so I'm extending UserProfile), so I can go to admin page and fill all the fields . But when I'm trying to do it from client side, my form just doesn't catch the data.
Also the strangest moment is that I can change username and email using UserChangeForm, so I'm trying to do the same for UserProfileObject.
models.py:
class UserProfile(models.Model):
user = models.OneToOneField(User)
image = models.ImageField(upload_to='profile_image', blank=True)
title = models.CharField(max_length=100, default = '')
first_name = models.CharField(max_length=200, default = '')
last_name = models.CharField(max_length=200, default = '')
subject = models.ManyToManyField('Subject', related_name='tutor_type', default = '', help_text="Select a subject")
AREA_STATUS = (
('Jerusalem', 'Jerusalem'),
('Tel Aviv', 'Tel Aviv'),
('Haifa', 'Haifa'),
('Eilat', 'Eilat')
)
area = models.CharField(max_length=200, choices=AREA_STATUS, blank=True, default='', help_text='Tutor area')
# Foreign Key used because tutor can only have one area, but area can have multiple tutors
# Author as a string rather than object because it hasn't been declared yet in file.
description = models.TextField(max_length=4000, help_text="Enter a brief description about yourself")
charge = models.IntegerField(default = '0')
# ManyToManyField used because Subject can contain many tutors. Tutors can cover many subjects.
# Subject declared as an object because it has already been defined.
LANGUAGE_CHOICES = (
('English','English'),
('Hebrew','Hebrew'),
('Russian','Russian'),
('French','French'),
('Arabic','Arabic'),
)
language = models.CharField('Language', choices = LANGUAGE_CHOICES, max_length=50, null=True)
def __str__(self):
return self.user.username
def display_subject(self):
"""
Creates a string for the subject. This is required to display subject in Admin.
"""
return ', '.join([ subject.name for subject in self.subject.all()[:3] ])
display_subject.short_description = 'Subject'
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender = User)
forms.py::
class EditProfileForm(UserChangeForm):
class Meta:
model = User
fields = (
'username',
'email',
'password'
)
class EditExtendedProfileForm(UserChangeForm):
class Meta:
model = UserProfile
fields = '__all__'
exclude = ('user',)
views.py:
def edit_profile(request):
if request.method == 'POST':
form = EditProfileForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
return redirect(reverse('accounts:view_profile'))
else:
form = EditProfileForm(instance=request.user)
args = {'form': form}
return render(request, 'accounts/edit_profile.html', args)
def edit_extended_profile(request):
if request.method == "POST":
form = EditExtendedProfileForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
return redirect(reverse('accounts:view_profile'))
else:
return redirect(reverse('accounts:edit_extended_profile'))
else:
form = EditExtendedProfileForm(instance = request.user)
args = {'form':form}
return render(request, 'accounts/edit_extended_profile.html', args)
edit_extended_profile.html:
{% extends "base.html" %}
{% block head %}
<title>Profile</title>
{% endblock %}
{% block body %}
<div class = "container">
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button type = "submit" class = "btn btn-success">Submit</button>
</form>
</div>
{% endblock %}
and it is the same template as for edit_profile view.
No traceback, no errors. Any help will be appreciated. Thanks in advance.
I'm trying to add a new feature to my existing app that let users create a profile and upload a pictures of their pets.
When a user login , he gets redirected into the profile which display his name and also he can add a picture of himself into the model which will get displayed on the profile page.
At the moment , I can retrieve the name into the template but I can't seem to display the user's name and upload picture at the same time.
Whenever I click Add picture , It doesn't let the user upload a picture instead I get this error
'PictureForm' object has no attribute 'save'
pet = form.save(commit =False) ...
I could design the page to let the user upload a picture but not display the name at the same time.
I think the problem lays in my profile.html and Profile function at views.py
Parts of my views.py
#login_required
def Profile(request):
Person = request.user.get_profile()
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/')
if request.method == "POST":
form = PictureForm(request.POST ,request.FILE or None)
if form.is_valid():
pet = form.save(commit =False)
pet.save()
context = (
{'Person': Person} ,
{'form':PictureForm()}
)
return render_to_response('profile.html', context, context_instance=RequestContext(request))
Parts of my forms.py
from django import forms
from django.contrib.auth.models import User
from django.forms import ModelForm
from pet.models import *
class PictureForm(forms.Form):
class Meta:
model = Person
fields = ('image')
My profile.html
{% if Person %}
<ul>
<li>Name : {{Person.name}} </li>
</ul>
{% endif %}
<form method="POST" enctype="multipart/form-data" "action" >
{% csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input type = "submit" value= "Add Picture" />
</form>
My models.py
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
class Person(models.Model):
user = models.OneToOneField(User)
name = models.CharField(max_length=100)
image = models.FileField(upload_to="images/",blank=True,null=True)
def __unicode__(self):
return self.name
class Pet(models.Model):
Person = models.ForeignKey(Person)
description = models.CharField(max_length=100)
image = models.FileField(upload_to="images/",blank=True,null=True)
def __unicode__(self):
return self.description
PictureForm needs to inherit from forms.ModelForm, not forms.Form.
Erase your form.save(commit=False). You will only do that if you override your save method
#login_required
def Profile(request):
Person = request.user.get_profile()
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/')
if request.method == "POST":
form = PictureForm(request.POST ,request.FILES)
if form.is_valid():
form.save()
context = (
{'Person': Person} ,
{'form':PictureForm()}
)
return render_to_response('profile.html', context, context_instance=RequestContext(request))
UPDATE:
[.....]
board = Board.objects.get(board=picture.board)//remove this
the_id = board.id //remove this
return HttpResponseRedirect(reverse('world:Boat', kwargs={'animal_id': picture.board.id })) // change the_id into picture.board.id
You have a typo. It should be request.FILES.
no buddy. your problem is in your model.py:
just add this function to your model
def save(self,*args, **kw):
super(PictureForm,self).save(*args, **kw)
Why my form is not filled with data from model?
This is my model.py
class People(models.Model):
user = models.OneToOneField(User)
name = models.CharField(max_length=100)
address = models.CharField(max_length=255)
This is my forms.py
from django.forms import ModelForm
class EditForm(ModelForm):
class Meta:
model = People
exclude=('user',)
views.py
def edit_data(request):
user = request.user
people = People.objects.get(user=user)
form = EditForm(request.POST, instance = people)
if request.method == 'POST':
if form.is_valid():
form.save()
else:
print 'Error'
else:
form = EditForm()
return render_to_response('profile.html',{'form':form}, context_instance=RequestContext(request))
profile.html
<form action="/profile/" method="post">{% csrf_token %}
{{ form.as_p }}
</form>
The problem is that you're redefining form in your else clause (to a new instance of your EditForm, which doesn't have the instance variable set). Remove the else (and the line under it) and you should be good to go.