I've been trying for days to understand the reason for my error, in the first phase I detected that it was missing {{ formset.management_form }} in the html, which i include and still continues to give the same error:
"ManagementForm data is missing or has been tampered with"
And I don't know if my views are the way they should be done.
Models
class Intervencao(models.Model):
........
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)
<!-- e
Forms
class TabletForm2(forms.ModelForm):
class Meta:
model=Intervencao
fields = __all__
class TabletFormImagem(forms.ModelForm):
imagem = forms.ImageField(required=True)
class Meta:
model=Imagem
fields = ['imagem',]
Views
def project_detail_chefe(request, pk):
ImagemFormSet = modelformset_factory(Imagem,form=TabletFormImagem, extra=3)
instance = Intervencao.objects.get(id=pk)
d1 = datetime.now()
intervencaoForm = TabletForm2(request.POST or None, instance=instance)
formset = ImagemFormSet(request.POST, request.FILES,queryset=Imagem.objects.none())
if request.method == "POST":
if intervencaoForm.is_valid() and formset.is_valid():
instance_form1 = intervencaoForm.save(commit=False)
instance_form1.data_intervencao_fim = d1
instance_form1.save()
images = formset.save(commit=False)
for image in images:
image.intervencao = instance_form1
image.save()
return redirect('index')
else:
intervencaoForm = TabletForm2(request.POST)
formset = ImagemFormSet(queryset=Imagem.objects.none())
context = {
'intervencaoForm':intervencaoForm,
'formset':formset,
}
return render(request, 'tablet/info_chefes.html', context)
HTML
<form method="post" enctype="multipart/form-data" id="gravar">
<div class="row">
<div class="col-md-12">
<table>
{{ formset.management_form }}
{% for form in formset %}
{{ form }}
{% endfor %}
</table>
</div>
</div>
</form>
in my case , the problem was the
else:
intervencaoForm = TabletForm2(request.POST)
formset = ImagemFormSet(queryset=Imagem.objects.none())
beacause is in line with the wrong if. all working now
Related
I have a problem validating django formsets when i build several formsets dynamically
In this case the one client could be various brands and contact people.
models.py
class Client(ChangesMixin, models.Model):
name = models.CharField(verbose_name="Nombre", max_length=100, unique=True)
code = models.PositiveIntegerField(verbose_name="Código", blank=True)
class Meta:
verbose_name = "Cliente"
verbose_name_plural = "Clientes"
class Brand(ChangesMixin, models.Model):
name = models.CharField(verbose_name="Marca", max_length=100, blank=True, null=True)
client = models.ForeignKey('Client', verbose_name="Cliente", related_name='brand_client', on_delete=models.DO_NOTHING)
class Meta:
verbose_name = "Marca"
verbose_name_plural = "Marcas"
class Contact(ChangesMixin, models.Model):
name = models.CharField(verbose_name="Contacto", max_length=100, blank=True, null=True)
client = models.ForeignKey('Client', verbose_name="Cliente", related_name='contact_client', on_delete=models.DO_NOTHING)
class Meta:
verbose_name = "Contacto"
verbose_name_plural = "Contactos"
I have two method to create forms and formsets dynamically
forms.py
def get_custom_formset(entry_model=None, entry_fields=None, action=None):
formset = None
if action == 'create':
extra = 1
else:
extra = 0
formset = modelformset_factory(
model = entry_model,
extra = extra,
form = get_custom_form(entry_model, entry_fields, action)
return formset
def get_custom_form(entry_model=None, entry_fields=None, action=None):
class _CustomForm(forms.ModelForm):
class Meta:
model = entry_model
fields = [field.name for field in entry_fields]
def __init__(self, *args, **kwargs):
"""
"""
super(_CustomForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.pk:
for field in entry_fields:
if action == 'detail':
self.fields[field.name].widget.attrs['readonly'] = True
return _CustomForm
I have a creation class view with get and post methods, depends on model passed.
I get the model fields to build the form and if a field is a foreign key i build formsets with these concrete model.
views.py
class CustomCreateView(LoginRequiredMixin, View, PermissionRequiredMixin):
model = None
template = 'create.html'
def get(self, request, *args, **kwargs):
template_form = str(self.model._meta.verbose_name).lower() + "_form.html"
model_fields = self.model._meta.get_fields()
form = None
formset = None
formsets = {}
for main_field in model_fields:
main_field_name = main_field.__class__.__name__
if main_field_name == 'ManyToOneRel':
model_names = str(main_field.name).split("_")
submodel = apps.get_model('app', model_names[0])
submodel_fields = submodel._meta.get_fields()
formset = app_forms.get_custom_formset(submodel, submodel_fields, 'create')
queryset = submodel.objects.none()
UPDATED with SOLUTION
formset = formset(queryset=queryset, prefix=submodel.__name__.lower())
formsets[submodel._meta.verbose_name.lower()] = formset
elif main_field_name == 'ManyToManyField':
print("NOT PROVIDED YET")
form = app_forms.get_custom_form(self.model, model_fields, 'create')
form = form(prefix=self.model.__name__.lower())
return render(request, self.template, {
'form': form,
'formsets': formsets,
'template_form': template_form,
})
def post(self, request, *args, **kwargs):
template_form = str(self.model._meta.verbose_name).lower() + "_form.html"
model_fields = self.model._meta.get_fields()
for main_field in model_fields:
main_field_name = main_field.__class__.__name__
if main_field_name == 'ManyToOneRel':
model_names = str(main_field.name).split("_")
submodel = apps.get_model('app', model_names[0])
submodel_fields = submodel._meta.get_fields()
formset = app_forms.get_custom_formset(submodel, submodel_fields, 'create')
queryset = submodel.objects.none()
formset = formset(queryset=queryset, prefix=submodel.__name__.lower())
formsets[submodel.__name__.lower()] = formset
elif main_field_name == 'ManyToManyField':
print("NOT PROVIDED YET")
form = app_forms.get_custom_form(self.model, model_fields, 'create')
form = form(request.POST, prefix=self.model.__name__.lower())
for prefix, formset in formsets.items():
formset = formset.__class__(request_post, prefix=prefix)
if formset.is_valid() and form.is_valid(): HERE THROWS THE ERROR
For templates i have 3 levels to build forms and formsets dynamically
create.html
{% get_url urls 'create' as element_create %}
<form class="" action="{% url element_create %}" method="POST">
{% csrf_token %}
{% include template_form %}
{% if formsets|length > 0 %}
{% for subtemplateformname, formset in formsets.items %}
{% include 'formset.html' %}
{% endfor %}
{% endif %}
</form>
formset.html
{{ formset.management_form }}
{% for form in formset %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% include 'form.html' %}
{% endfor %}
form.html
{% load widget_tweaks %}
{% for field in form %}
<div class="form-group{% if field.errors %} has-error{% endif %}">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field|add_class:"form-control" }}
{% for error in field.errors %}
<p class="help-block">{{ error }}</p>
{% endfor %}
</div>
{% endfor %}
Firstly, when you get a formset for a related model you pass sub_fields but I have no idea where this is coming from?
formset = app_forms.get_custom_formset(submodel, sub_fields, 'create')
The error is with the way you are defining the formset prefix. In the GET you are passing model_names[0], which for a normal relationship will be the model name lowercase without any spaces. Lets use a model named MyModel for example
main_field.name # 'mymodel_set'
model_names = str(main_field.name).split("_") # ['mymodel', 'set']
model_names[0] # 'mymodel'
formset = formset(queryset=queryset, prefix=model_names[0])
When you assign the formset to the formsets dictionary you are using something different even though you are treating it the same
formsets[submodel._meta.verbose_name.lower()] = formset
...
for prefix, formset in formsets.items():
formset = formset.__class__(request_post, prefix=prefix)
submodel._meta.verbose_name will return the model name with spaces. So any models that have 2 "words" in it's verbose name will not set the correct prefix (e.g. MyModel._meta.verbose_name.lower() == 'my model' but model_names[0] == 'mymodel')
To new readers: Neverwalkaloner's solution solved the initial error but the photo upload is still required and making required false in forms.py gives me a MultiValueDictKeyError. Any help on making it optional would be greatly appreciated.
I have a model and form to upload either a picture and text, or just text. My intention, actually was to make it a choice between an image, text or both and any help with that would be appreciated, but I digress. Uploading only works when an image is included, if it is just text, I get the error:
The view lesyeux.views.posts didn't return an HttpResponse object. It
returned None instead.The view lesyeux
My model is:
class Post(models.Model):
image = models.ImageField(upload_to='uploaded_images', blank=True,
null=True)
text_post = models.CharField(max_length=1000)
author = models.ForeignKey(User)
My form is:
class PostForm(forms.ModelForm):
image = forms.FileField(label='Select an image file',
help_text='Please select a photo to upload')
text_post = forms.CharField(help_text="Please enter some text.")
class Meta:
model = Post
fields = ('image', 'text_post',)
exclude = ('author',)
My view is:
def posts(request, id=None):
neighborhood = get_object_or_404(Neighborhood, id=id)
form = PostForm()
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
post = Post(image = request.FILES['image'])
post = form.save(commit=False)
post.author = request.user
post = post.save()
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
else:
form = PostForm()
posts = Post.objects.all().order_by('-id')
return render(request, 'posts.html', context = {'form':form,
'posts':posts, 'neighborhood':neighborhood})
and my form is:
<form id="PostForm" method="post" action="/view/{{ neighborhood.id }}/posts/" enctype="multipart/form-data">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<input type="hidden" name="next" value="{{ request.path }}">
<input type="submit" name="submit" value="Post" />
</form>
Your view doesnt return response if form is not valid. To fixt it rewrite view like this:
def posts(request, id=None):
neighborhood = get_object_or_404(Neighborhood, id=id)
form = PostForm()
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
post = Post(image = request.FILES['image'])
post = form.save(commit=False)
post.author = request.user
post = post.save()
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
else:
form = PostForm()
posts = Post.objects.all().order_by('-id')
return render(request, 'posts.html', context = {'form':form, 'posts':posts, 'neighborhood':neighborhood})
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 %}
It's my first time with formsets / images and this is my error:
KeyError at /houses/new/
'image'
This is my code:
models.py
class House(models.Model):
user = models.ForeignKey(User, related_name='houses', on_delete=models.CASCADE)
address = models.CharField(max_length=500)
type = models.CharField(default='House', max_length=100)
stories = models.IntegerField()
square_feet = models.IntegerField()
description = models.TextField()
# Class is for the houses images
class Image(models.Model):
house = models.ForeignKey(House, default=None, related_name="images", on_delete=models.CASCADE)
image = models.ImageField(verbose_name='image')
forms.py
# This is the blueprint for House forms
class AlbumForm(forms.ModelForm):
address = forms.CharField(label="Address:")
type = forms.CharField(label="Type of House (House, Condo, Cottage):")
stories = forms.IntegerField(label="House Stories:")
square_feet = forms.IntegerField(label='Square Feet:')
class Meta:
model = House
fields = ['address', 'type', 'stories', 'square_feet', 'description']
# This is the form for images
class ImageForm(forms.ModelForm):
image = forms.ImageField(label='Image')
class Meta:
model = Image
fields = ('image',)
views.py
def post_house(request):
ImageFormSet = modelformset_factory(Image, form=ImageForm, extra=10)
if request.method == 'POST':
house_form = AlbumForm(request.POST)
formset = ImageFormSet(request.POST, request.FILES, queryset=Image.objects.none())
if house_form.is_valid() and formset.is_valid():
post_form = house_form.save(commit=False)
post_form.user = request.user
post_form.save()
for form in formset.cleaned_data:
image = form['image']
photo = Image(house=post_form, image=image)
photo.save()
messages.success(request, "New house listing success")
house = post_form
return redirect('houses:details', house_id=house.pk)
else:
return render(request, 'login.html')
else:
house_form = AlbumForm()
formset = ImageFormSet(queryset=Image.objects.none())
return render(request, 'houses/house_form.html', {'house_form': house_form, 'formset': formset})
house_form.html
{% extends 'base.html' %}
{% block content %}
<br>
<div class="container">
<h4>Post a New Home</h4>
<form id="post_form" method="post" action="" enctype="multipart/form-data">
{% csrf_token %}
{{house_form}}
{{ formset.management_form }}
{% for form in formset %}
{{ form }} <br>
{% endfor %}
<input type="submit" name="submit" class="btn btn-success" value="Submit" />
</form>
</div>
{% endblock %}
It must be some relation between my form key and what each form in formset is taking. That being said they're both 'image' so I don't see the problem. Please let me know if you got an idea. Thanks a ton!
Try the below code,
if formset.is_valid():
for form in formset:
data = form.cleaned_data
image = data.get('image')
photo = Image(house=post_form, image=image)
photo.save()
I already have codes and it doesn't working. But I'am looking for solution or practice on how to pass data between pages with using form. Should I work with hiddenfields or sessions?. I am ready to change or rewrite my codes. I am open the other suggestions, thought.
Here is my codes
forms.py
class applyForm(forms.ModelForm):
basvuru_mesaji = forms.CharField(required=True,error_messages={'required':'Bu alanı boş bırakmamanız gerekiyor'}, widget=forms.Textarea(attrs={'class': 'full','placeholder': 'Başvuru mesajınızı buraya yazınız'}))
tcn = forms.IntegerField(required=False, error_messages={'invalid':'Numara dışında karakter girişi yaptınız'}, widget=forms.TextInput(attrs={'class': 'full','placeholder': 'T.C. Kimlik Numaranız'}))
hidden = forms.CharField(widget=forms.HiddenInput(), required = False)
class Meta():
model = apply
fields = '__all__'
models.py
class apply(models.Model):
tcn = models.PositiveIntegerField(blank=True, verbose_name='T.C.N :', null=True)
basvuru_mesaji = models.TextField(verbose_name='Başvuru Mesajı')
views.py
def form_for_apply(request):
form = applyForm(request.POST or None)
if form.is_valid():
new_join = form.save(commit=True)
new_join.save()
basvuru_mesaji = form.cleaned_data["basvuru_mesaji"]
return HttpResponseRedirect('/apply_ok/')
context = {"form":form}
template = "apply.html"
return render(request, template, context)
def apply_ok(request):
allfield = apply.objects.all()
print allfield
form = applyForm(request.GET)
context = {"form":form}
#print context.data
template = "apply_ok.html"
return render(request, template, context)
apply.html
{% csrf_token %}
{% if form.errors %}
{{ form.errors }}
{% endif %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
<div class="mTopTwe">
<label for="{{ form.basvuru_mesaji.id_for_label }}">
{{form.basvuru_mesaji.field.label}}
</label>
{{ form.basvuru_mesaji.errors }}
{{ form.basvuru_mesaji }}
</div>
apply_ok.html
<div class="mTopTwe">
{{form.basvuru_mesaji.data}} {{ form.hidden.data }}
</div>
You can using django wizard-form for passing data between pages.
render_to_response solved my problem.
return render_to_response('apply_ok.html', context_instance=RequestContext(request,{'form':form}))
forms.py
def apply(request):
form = applyForm(request.POST or None)
if form.is_valid():
new_join = form.save(commit=True)
new_join.save()
basvuru_mesaji = form.cleaned_data['basvuru_mesaji']
return render_to_response('apply_ok.html', context_instance=RequestContext(request,{'form':form}))
context = {"form":form}
template = "apply.html"
return render(request, template, context)