I can't figure out how to store a simple form in the database. I think I'm quite close but there is probably something wrong in my views.py. Here is my code, any ideas what I'm doing wrong? (also on dpaste)
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
# forms.py
class CategoryForm(forms.Form):
name = forms.CharField(max_length=30)
# views.py
#login_required
def newCategory(request):
if request.method == 'POST':
username = request.user.username
cform = CategoryForm(request.POST)
if cform.is_valid():
formInstance = cform.save(commit = False)
formInstance.user = username
formInstance.name = cform.cleaned_data['name']
formInstance = IngredienceCategory.objects.filter(name=formInstance.name, user=formInstance.user)
formInstance.save()
# return HttpResponseRedirect('new-category/')
else:
form = CategoryForm()
context = {'form': form}
return render_to_response('new-category.html', context, context_instance=RequestContext(request))
# new-category.html
<h3>Insert New Category</h3>
<form action="/" method="post" id="food-form">{% csrf_token %}
{{ form.as_p }}
<input type="submit" name="foodForm" value="Save" />
</form>
The line below is not useful at it current position. That command will perform a database query and assign the result as a queryset, before you have saved the form data.
formInstance = IngredienceCategory.objects.filter(name=formInstance.name, user=formInstance.user)
This should work:
With cform as a normal Form:
if cform.is_valid():
formInstance = IngredienceCategory(user=request.user, cform.cleaned_data['name'])
formInstance.save()
If cform had been a ModelForm you could do:
if cform.is_valid():
formInstance = cform.save(commit=False)
formInstance.user = request.user
formInstance.save()
I do recommend you to check out ModelForms since it will build the cleaning functionality based on your model.
You should inherit from ModelForm
from django.forms import ModelForm
class CategoryForm(ModelForm):
class Meta:
model = IngredienceCategory
Refer to https://docs.djangoproject.com/en/dev/topics/forms/modelforms/ for how to render form and save it to database.
Related
I have simple cv upload class for users to upload their resume. it works just fine but when they upload a newer one, the previous wont get deleted.
this is my code:
class ResumeDocument(models.Model):
id = models.AutoField(primary_key=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
cvfile = models.FileField(upload_to="documents", null=True, validators= [validate_file_extension])
#property
def filename(self):
return os.path.basename(self.cvfile.name)
how can I reach the previous id? id = self.id - 1. something like that.
this is my views:
#login_required
def pdf_resume(request):
if request.method == 'POST':
form = DocumentForm(request.POST,request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect('pdf_resume')
if 'delete' in request.GET:
return delete_item(ResumeDocument, request.GET['id'])
else:
form = DocumentForm()
documents = ResumeDocument.objects.filter(user=request.user)
if documents:
form = DocumentForm(instance=documents[0])
context = {
'form':form,
'documents':documents,
}
return render(request, 'reg/pdf_resume.html', context)
and this is also my HTML code:
<form id="document-form" method="POST" enctype="multipart/form-data" action="{% url 'pdf_resume' %}" autocomplete="off" class="ant-form ant-form-horizontal">
{% csrf_token %}
<p>{{ form }}</p>
<div class="ant-row-flex" style="margin-left: -10px; margin-right: -10px;"></div>
<button id="btn_submit" type="submit"
class="ant-btn ant-btn-primary"
ant-click-animating-without-extra-node="false" style="float: left;"><span>upload</span></button>
</form>
One possible solution is to save all uploaded CVs for each user and track the last uploaded CV with field uploaded_on. You may refer to the below high-level example:
class CVFile(models.Model):
file_name = models.CharField(max_length=200)
cv_file = models.FileField(upload_to="documents", null=True, validators=[validate_file_extension])
uploaded_on = models.DateTimeField(default=timezone.now, blank=True)
class ResumeDocument(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
cv_file = models.ForeignKey(CVFile, on_delete=models.CASCADE)
You will need to amend your HMTL and views.py accordingly.
models.py
class ResumeDocument(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
cvfile = models.FileField(upload_to="documents", null=True, validators=
[validate_file_extension])
views.py(i just look is there any resumedocuments for that user if yes so i give him
the form just if he was editing the existing file)
#login_required
def pdf_resume(request):
if request.method == 'POST':
form = DocumentForm(request.POST,request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect('pdf_resume')
if 'delete' in request.GET:
return delete_item(ResumeDocument, request.GET['id'])
else:
form = DocumentForm()
documents = ResumeDocument.objects.filter(user=request.user)
if documents:#new
form = DocumentForm(instance=documents[0])
context = {
'form':form,
'documents':documents,
}
return render(request, 'reg/pdf_resume.html', context)
now inside your pdf_resume.html your can just pass the 'form' because you already check if that user has a document or not.
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 am trying to save simple form add_product, I don't have any error but the new product doesn't appear in admin or on a page. I really not sure what I am doing wrong. Any suggestion would be great! Thank you.
my views.py
def add_product(request):
author = request.user
product_form = ProductForm(request.POST, request.FILES, instance=author)
if product_form.is_valid():
form = product_form.save(commit=False)
form.save()
return HttpResponseRedirect('/products/')
else:
product_form = ProductForm()
return render(request, 'products/add_product.html', {'product_form': product_form})
my forms.py
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ('title', 'content', 'picture',)
urls.py
urlpatterns = patterns('',
url(r'^$', views.all, name='all'),
url(r'^add/$', views.add_product, name='add_product'),
url(r'^(?P<slug>[\w-]+)/$', views.single_product, name='single_product'),
)
template: products/add_product.html
<h1>Add Product</h1>
<form method="post" action="" enctype="multipart/form-data"> {% csrf_token %}
{{ product_form.as_p }}
<input type="submit" value="Add">
</form>
models.py
class Product(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(User)
content = models.CharField(max_length=300)
slug = models.SlugField(unique=True)
picture = models.ImageField(upload_to='products/picture/', blank=True)
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('single_product', kwargs={'slug': self.slug})
I fixed my view.py by adding following line :
form.author = request.user
and I removed the first line : author = request.user plus the instance as well.
So now works fine :)
Your code doesn't seem to be valid. And there should be errors.
The first thing that is notice is following:
author = request.user
product_form = ProductForm(request.POST, request.FILES, instance=author)
You are passing user instance as a instance to author form. That doesnt make any sense. For product form instance should a product, not a user. If you want to set a author field you can do following :
form = product_form.save(commit=False)
form.author = author
form.save()
Also its not good to call this variable form, because its not a form anymore, its a product object that is returned by save function.
I'm really stuck with this. To show my problem I created a new Django project and started from scratch, focusing only on one single form.
What I'm trying to do is to create a form with several fields of the same name. I tried using modelformset_factory to achieve this but it looks to me like it's not what I really need.
Below is my code (also on dpaste) which currently works fine with one single field called name. How can I create and process a form which would have several name fields? Could somebody point me in the right direction?
# models.py
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, blank=True, null=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
# forms.py
class CategoryForm(ModelForm):
class Meta:
model = Category
fields = ('name',)
# views.py
def home(request):
if request.method == 'POST':
catform = CategoryForm(request.POST)
catformInstance = catform.save(commit = False)
catformInstance.save()
return HttpResponseRedirect('')
else:
catform = CategoryForm()
context = {'catform': catform}
return render_to_response('home.html', context, context_instance=RequestContext(request))
# home.html template
<h3>Insert new Category</h3>
<form action="/" method="post" id="ingr-cat-form">{% csrf_token %}
{{ catform.as_p }}
<input type="submit" name="ingrCatForm" value="Save" />
</form>
UPDATE: to clarify, I want to allow user to insert several categories within one form. I think I'm getting close, here is my new version of views.py but it still stores just one category (the last one in the list):
def home(request):
if request.method == 'POST':
catform = CategoryForm(request.POST)
names = request.POST.getlist('name')
catformInstance = catform.save(commit = False)
for name in names:
catformInstance.name = name
catformInstance.save()
return HttpResponseRedirect('')
else:
catform = CategoryForm()
context = {'catform': catform}
return render_to_response('home.html', context, context_instance=RequestContext(request))
You cannot have fields with the same name (on the same Model). If you only need to change the html label in the html form, use
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
name2 = models.CharField(max_length=30, unique=True, verbose_name="name")
user = models.ForeignKey(User, blank=True, null=True)
or
class CategoryForm(ModelForm):
def __init__(self , *args, **kwargs):
super(CategoryForm, self).__init__(*args, **kwargs)
self.fields['name2'].label = "name"
Here is a working solution. Thanks to #YardenST for pointing me in the right direction. I managed to solve my initial problem by following this tutorial.
# models.py
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, blank=True, null=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
# forms.py
class CategoryForm(ModelForm):
class Meta:
model = Category
fields = ('name',)
# views.py
def home(request):
if request.method == 'POST':
catforms = [CategoryForm(request.POST, prefix=str(x), instance=Category()) for x in range(0,3)]
if all([cf.is_valid() for cf in catforms]):
for cf in catforms:
catformInstance = cf.save(commit = False)
catformInstance.save()
return HttpResponseRedirect('')
else:
catform = [CategoryForm(prefix=str(x), instance=Category()) for x in range(0,3)]
context = {'catform': catform}
return render_to_response('home.html', context, context_instance=RequestContext(request))
# home.html template
<h3>Insert new Category</h3>
<form action="/" method="post" id="ingr-cat-form">{% csrf_token %}
{% for catform_instance in catform %} {{ catform_instance.as_p }} {% endfor %}
<input type="submit" name="ingrCatForm" value="Save" />
</form>
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.