I have a standard Form to edit an object that has a file field in it. When I add a new profile, everything works fine, but when I want to modify a field, then problems happen: when I don't include request.FILES, all fields get retrieved in the form and can be updated with no problems, except the avatar (filefield), but when I add the request.FILES, fields are no longer retrieved in the inputs, and the form is not valid. What am I doing wrong?
models.py:
from django.db import models
class Profile(models.Model):
name = models.CharField(max_length=45)
email = models.EmailField()
avatar = models.FileField(upload_to="avatars/")
def __str__(self):
return self.name
forms.py:
from django import forms
from .models import Profile
class EditUserForm(forms.ModelForm):
class Meta:
model = Profile
fields = ["name", "email", "avatar"]
profile.html:
<form method="POST">
{% csrf_token %}
<div class="col-md-12">
<div class="row border m-3 p-5">
<div class="col-md-6">Name</div>
<div class="col-md-6">{{ form.name }}</div>
</div>
<div class="row border m-3 p-5">
<div class="col-md-6">Email</div>
<div class="col-md-6">{{ form.email }}</div>
</div>
<div class="row border m-3 p-5">
<div class="col-md-6">Avatar</div>
<div class="col-md-6">
<img src="{{ profile.avatar.url }}" width="100rem" />
{{ form.avatar }}
</div>
</div>
<div class="row">
<button type="submit" class="btn btn-success">Save</button>
</div>
</div>
</form>
views.py:
def profile(request, pk):
profile_instance = Profile.objects.get(id=pk)
form = EditUserForm(request.POST, request.FILES, instance = profile_instance)
context = {
'profile': profile_instance,
'form': form,
}
if request.method == "POST":
if form.is_valid():
profile = form.save(commit=False)
profile.save()
else:
print("form not valid")
else:
return render(request, "app/profile.html", context)
return render(request, "app/profile.html", context)
Special thank's to #bruno desthuilliers and #dirkgroten.
Here is the answer to solve the 2 problems:
def profile(request, pk):
if request.method == 'POST':
form = EditUserForm(request.POST, request.FILES, instance = Profile.objects.get(id=pk))
if form.is_valid():
profile = form.save(commit=False)
profile.save()
return redirect('profile', pk)
else:
form = EditUserForm(instance = Profile.objects.get(id=pk))
context = {
'profile': Profile.objects.get(id=pk),
'form': form,
}
return render(request, "app/profile.html", context)
Try refactoring the code as follows: (edit it if I missed something. After form.is_valid, the same form should be sent in the context to display the errors.
def profile(request, pk):
profile_instance = Profile.objects.get(id=pk)
form = EditUserForm(instance = profile_instance)
if request.method == "POST":
form = EditUserForm(request.POST, request.FILES, instance = profile_instance)
if form.is_valid():
profile = form.save(commit=False)
profile.save()
form = EditUserForm(instance = profile_instance)
context = {
'profile': profile_instance,
'form': form,
}
return render(request, "app/profile.html", context)
Related
I'm trying to make model based form but something went wrong.
model:
class Topic(models.Model):
name = models.CharField(max_length=200)
icon = models.ImageField(upload_to = 'images/')
form:
class TopicCreationForm(ModelForm):
class Meta:
model = Topic
fields = '__all__'
view:
def TopicCreateView(request):
form = TopicCreationForm()
if request.method == 'POST':
form = TopicCreationForm(request.POST)
if form.is_valid():
form.save()
return redirect('home')
else:
print('aaa') # It displays in console
context = {'form':form}
return render(request, 'blog/topic_form.html', context)
my form html part
<form method="POST">
{% csrf_token %}
<fieldset >
<legend> New Topic</legend>
{{ form|crispy }}
</fieldset>
<div>
<input type="submit" value="submit" class="button-33" role="button">
</div>
</form>
where did i make mistake ?
You need to pass both request.POST and request.FILES [Django-doc], so:
def topic_create(request):
if request.method == 'POST':
form = TopicCreationForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('home')
else:
print('aaa') # It display in console
else:
form = TopicCreationForm()
context = {'form':form}
return render(request, 'blog/topic_form.html', context)
In the HTML form, you need to specify that the files should be encoded with the enctype="…" attribute [mdn]:
<form method="post" enctype="multipart/form-data">
…
</form>
I have two models with two different forms. One has a ForeignKey for another model, making a 1-N relationship. The problem is when i try to add images , is not working. The form where i change the (name/TabletForm2) is working so , only when i try to add (image/TabletFormImagem) is not working.
The problem is that
My model's
def get_image(instance, filename):
return os.path.join('intervencao/fotografias', str(instance.intervencao), filename)
class Intervencao(models.Model):
name= models.CharField(verbose_name="Name", max_length=200, blank=True, null=True)
class Imagem(models.Model):
intervencao = models.ForeignKey(Intervencao, related_name='intervencaoObjectsImagem', on_delete=models.CASCADE)
imagem = models.ImageField(upload_to=get_image, blank=True, null=True, verbose_name="Fotografia")
def __str__(self, ):
return str(self.intervencao)
My View
def project_detail_chefe(request, pk):
form = TabletForm2(request.POST)
form2 = TabletFormImagem(request.POST, request.FILES)
if request.method == "POST":
if form.is_valid():
form.save()
return redirect('index')
else:
form = TabletForm2(request.POST)
if form2.is_valid():
//when i print something here to see if the form2 is valid , never enter here.
form2.save()
return redirect('index')
else:
form2 = TabletFormImagem()
context = {
'form':form,
'form2':form2,
}
return render(request, 'tablet/info_chefes.html', context)
tablet/info_chefes.html
<div class="col-md-12">
<div class='form-group'>
<label for="{{ form.subject.id_for_label }}" id="titulo">Name:</label>
<em>{{ form.name}}</em>
</div>
</div>
<div class="col-md-12">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form2.as_p }}
<button type="submit">Upload</button>
</form>
</div>
Forms
class TabletForm2(forms.ModelForm):
class Meta:
model=Intervencao
fields = ['data_intervencao_fim','ferramenta']
class TabletFormImagem(forms.ModelForm):
class Meta:
model=Imagem
fields = ['imagem']
def project_detail_chefe(request, pk):
form = TabletForm2()
form2 = TabletFormImagem()
if request.method == "POST":
form = TabletForm2(request.POST)
form2 = TabletFormImagem(request.POST, request.FILES)
if form.is_valid() and form2.is_valid():
instance_form1 = form.save()
instance_form2 = form2.save(commit=False)
instance_form2.intervencao = instance_form1
instance_form2.save()
return redirect('index')
else:
form = TabletForm2()
form2 = TabletFormImagem()
context = {
'form':form,
'form2':form2,
}
return render(request, 'tablet/info_chefes.html', context)
HTML
<form method="post" enctype="multipart/form-data">
<div class="col-md-12">
<div class='form-group'>
<label for="{{ form.subject.id_for_label }}" id="titulo">Name:</label>
<em>{{ form.name}}</em>
</div>
</div>
<div class="col-md-12">
{% csrf_token %}
{{ form2.as_p }}
<button type="submit">Upload</button>
</div>
</form>
I have main model Entertainment and related model EntertainmentCollage. Now i'm doing edditing page for my models in which I will need to transfer for editing both models.
I understand how to transfer one form to the form but with a related model I have difficulty.
class Entertainment(models.Model):
main_photo = models.ImageField(upload_to = 'where/')
place = models.CharField(max_length=200)
description = models.CharField(max_length=200)
event_date = models.DateTimeField(auto_now_add=False, blank=True, null = True)
class EntertainmentCollage(models.Model):
img = models.ImageField(upload_to = 'entertainment/portfolio', blank = True)
album = models.ForeignKey(Entertainment, blank = True, null = True)
forms.py
class WhereCreateForm(ModelForm):
class Meta:
model = Entertainment
fields = ['main_photo','place','description', 'event_date' ]
views.py
def edit_where(request, pk):
place = Entertainment.objects.get(id=pk)
form = WhereCreateForm(instance=place)
if request.user.is_authenticated():
if request.method == "POST":
form = WhereCreateForm(request.POST, request.FILES, instance=place)
if form.is_valid():
form.save()
return redirect('entertainment:where_list')
else:
form = WhereCreateForm()
return render(request, "entertainment/where_edit.html", {'form': form})
html
<form method = "post">
{% csrf_token %}
<p>{{ form.description }}</p>
<p>{{ form.place }}</p>
<p>{{ form.event_date }}</p>
</div>
<div class="col-md-9">
<section class="admin-section">
<div class="row">
<div class="col-md-4 admin__block" is-cover="false">
<div class="cover__wrapper edit__wrapper">
<a class="delete-button">Delete</a>
<a class="make-cover-button">Cover</a>
<img src="img/place-placeholder-1.jpg" alt="">
</div>
</div>
</div>
Add photo
</section>
<section>
<h4>Description</h4>
{{ form.description }}
Save
Cancel
</section>
</form>
As #art06 said in his comment you can use inline formsets.
You can do something like this:
from django.forms import inlineformset_factory
def edit_where(request, pk):
place = Entertainment.objects.get(id=pk)
FormSet2 = inlineformset_factory(Entertainment, EntertainmentCollage)
form = WhereCreateForm(instance=place)
form2 = FormSet2()
if request.user.is_authenticated():
if request.method == "POST":
form = WhereCreateForm(request.POST, request.FILES, instance=place)
form2 = FormSet2(request.POST or None, instance=place)
if form.is_valid():
if form2.is_valid():
form.save()
form2.save()
return redirect('entertainment:where_list')
else:
form = WhereCreateForm()
form2 = FormSet2()
return render(request, "entertainment/where_edit.html", {'form': form, 'form2': form2})
To add new form into template you can use:
{{ form2.management_form }}
{% for frm in form2 %}
{{ frm.as_table }}
{% endfor %}
Halo, i'm trying to upload a file using filefield. But i always failed. when statement form.errors.as_data() executed, the browser return 'tempfile'. I already trying to find solution from django documentation and some django references. But, still can't fix it. ;(
Here's my view.py
def dataprocessing(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
import pdb; pdb.set_trace()
newdoc = Document(docfile=request.FILES['myfile'])
newdoc.save()
#Redirect to the dataprocessing after POST
#return render(request, 'dataprocessing.html')
return HttpResponse("success")
else:
return HttpResponse(form.errors.as_data())
else:
import pdb; pdb.set_trace()
form = DocumentForm() #A empty, unbound form
return render(request, 'dataprocessing.html', {'form': form})
models.py
class Document(models.Model):
docfile = models.FileField(upload_to='documents/%Y/%m/%d')
forms.py
class DocumentForm(forms.Form):
tempfile = forms.FileField()
And dataprocessing.html
<form method="post" enctype="multipart/form-data" action="{% url "dataprocessing" %}">
<div class="form-group">
<label for="up">Input Data</label> {% csrf_token %}
<input type="file" name=myfile class="filestyle" data-buttonName="btn-primary" data-buttonBefore="true" data-size="sm" accept="application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
id="up">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block">Upload Data</button>
<button type="button" class="btn btn-primary btn-block">Download Template</button>
</div>
</form>
How about using forms.ModelForm instaed forms.Form like this?
# forms.py
class DocumentForm(forms.Model):
class Meta:
model = Document
fields = ['tempfile']
and make your views.py like this:
# views.py
def dataprocessing(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponse("success")
else:
return HttpResponse(form.errors.as_data())
else:
form = DocumentForm() #A empty, unbound form
return render(request, 'dataprocessing.html', {'form': form})
This makes form object can be saved directly to your model.
I've made a profile model in models.py:
class Profile(models.Model):
user = models.OneToOneField(User)
title = models.CharField(max_length=20, default='title')
firstname = models.CharField(max_length=40, default='firstname')
lastname = models.CharField(max_length=40, default='lastname')
blurb = models.CharField(max_length=500, default='tell us about yourself')
#work out how to make filename equal the username
pic = models.ImageField(default="static/profile/blank.png", upload_to='static/profile/%d%m%y.jpg') #+ user.pk + '.jpg')
def __unicode__(self):
return self.user.username
and here is the view for a page to edit the profile of a logged in user:
def editprofile(request):
u_p = request.user.profile
template = loader.get_template('registration/editprofile.html')
if request.method == 'POST':
form = ProfileForm(request.POST, instance=u_p)
if form.is_valid():
form.save()
else:
# todo
None
else:
#todo
context = RequestContext(request, {'form': form})
return HttpResponse(template.render(context))
The template fragment reads:
<form method="POST" action=".">
{% csrf_token %}
<div class="regformout">
<div class="regform">
{% for field in form %}
<div class='cell'> {{ field.label_tag }} </div>
<div class='nogin'> {{ field.errors }} </div>
<div class='cell'> {{ field }} </div>
{% endfor %}
</div>
</div>
<input class="btn btn-large btn-primary" type="submit" value="Save Your Profile" ></input>
</form>
I want the form fields to automatically populate with the data for the current user on the corresponding page for editing the profile. However, no matter what I try I cannot make this happen. What am I doing wrong here?
Your main problem is you are only populating the form if the user hits the submit button, so when the view is requested initially, your form is empty.
from django.shortcuts import render, redirect
def editprofile(request):
u_p = request.user.profile
form = ProfileForm(request.POST or None, instance=u_p)
if request.method == 'POST':
if form.is_valid():
form.save()
return redirect('/')
return render(request,
'registration/editprofile.html', {'form': form})