form is not valid... WHY? - django

the model:
class Operation(models.Model):
operation_type = models.ForeignKey(OperationType)
category = models.ForeignKey(Category, default=0)
related_account = models.ForeignKey(Account, related_name = 'related_account', null = True)
comments = models.TextField(null = True)
the code:
def detail(request, category_id):
class OperationCategoryOnlyForm(forms.ModelForm):
class Meta:
model = Operation
fields = ('operation_type', 'category', 'related_account', )
from django.forms.models import modelformset_factory
OperationFormSet = modelformset_factory(Operation, form=OperationCategoryOnlyForm)
if request.method == "POST":
formset = OperationFormSet(request.POST, queryset=Operation.objects.filter(category=category_id))
if formset.is_valid():
formset.save()
# HERE IS THE FORMSET, WHICH OVERLAPS THE POSTED FORMSET - this was intentionaly to get the current result not result before save, but when you want to debug - you should pay attention at such things.
# formset = OperationFormSet(queryset=Operation.objects.filter(category=category_id))
c = {"formset" : formset,}
c.update(csrf(request))
return render_to_response("reports/operation_list.html", c)
the template: UPDATED:
<form method="post" action="">
{% csrf_token %}
{{ formset.management_form }}
{{ formset.errors }}
{{ formset.non_field_errors }}
{{ formset.non_form_errors }}
<table>
{% for form in formset.forms %}
tr><td> {{ form.errors }} </td><td> {{ form.non_field_errors }}</td></tr>
<tr><td>{{ form.id }}</td><td>{{ form.instance.comments }}</td><td>{{ form.operation_type }}<br>{{ form.related_account }}</td><td>{{ form.category }}</td></tr>
{% endfor %}
</table>
<input type="submit" value="submit">
</form>
I found that form.is_valid() = false - but i have no idea how to get the reason WHY...
UPD - I updated the template according to comment - nothing is result....
(this is very stupid rule, that i have to write less code than question - code is self explaining and is the essence of question - Almost always the question reduces into one sentence but there's no way to reduce code)

Yeah, because you're creating another, empty formset before displaying it. Add print formset.errors before or directly after if checking for is_valid().

Related

Django: Create an editable form for each instance within a queryset all in one page?

Sorry if this is too much code, but I believe it is all relevant to the question at hand.
Long story short, on my series_detail page, all episodes belonging to each series is shown, as well as forms to add a new episode or edit an existing one.
The edit episode form, however, requires an instance, which always returns the very first object of the episodes queryset. This is presumably because of the .first(), but I used this since you can only have one object passed as an instance.
What I am trying to achieve is:
after showing the edit modal next to each episode, show the instance of each episode instead of only the first episode.
save only that episode's instance after the form is filled
achieve this without redirecting to an edit page
models.py
class Series(models.Model):
name = models.CharField(max_length=100)
class Episode(models.Model):
series = models.ForeignKey(Series, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
episode_no = models.IntegerField(null=True)
description = models.TextField(max_length=500)
image = models.ImageField(upload_to='pics/episodes',)
forms.py
class EpisodeForm(forms.ModelForm):
name = forms.CharField()
description = forms.CharField(widget=forms.Textarea, required=False)
episode_no = forms.IntegerField()
class Meta:
model = Episode
fields = ['name', 'description', 'episode_no' ,'image']
views.py
def series_detail(request, pk):
try:
series = Series.objects.get(pk=pk)
except:
return render(request, 'error_404.html')
episodes = Episode.objects.filter(series=series).first()
if request.method == 'POST':
if 'addepisodeform' in request.POST:
e_form = EpisodeForm(request.POST, request.FILES, prefix='addepisode')
e_form.instance.series = Series.objects.get(pk=pk)
if e_form.is_valid():
e_form.save()
return redirect('series_detail', pk=pk)
messages.success(request, 'Episode was created')
else:
return redirect('series_detail', pk=pk)
messages.error(request, 'Episode was not created')
elif 'editepisodeform' in request.POST:
edit_e_form = EpisodeForm(request.POST, request.FILES, instance=episodes, prefix='editepisode')
edit_e_form.instance.series = Series.objects.get(pk=pk)
if edit_e_form.is_valid():
edit_e_form.save()
return redirect('series_detail', pk=pk)
messages.success(request, 'Episode was updated')
else:
return redirect('series_detail', pk=pk)
messages.error(request, 'Episode was not updated')
else:
e_form = EpisodeForm(prefix='addepisode')
edit_e_form = EpisodeForm(instance=episodes, prefix='editepisode')
context = {
'episodes': episodes,
'e_form': e_form,
'edit_e_form': edit_e_form
}
return render(request, 'series/series_detail.html', context)
def delete_episode(request, pk1, pk2):
try:
series = Series.objects.get(pk=pk1)
except:
return render(request, 'error_404.html')
try:
episode = Episode.objects.get(series=series, episode_no=pk2)
except:
return render(request, 'error_404.html')
episode.delete()
return redirect('series_detail', pk=pk1)
urls.py
path('series/<int:pk>/', views.series_detail, name='series_detail'),
path('series/<int:pk1>/episode/<int:pk2>/delete/', views.delete_episode, name='delete_episode'),
series_detail.html
<button type="submit" name="addepisodeform">
Add Episode
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ e_form }}
</form>
</button>
{% for episode in episodes %}
{{ episode.name }}
{{ episode.description}}
<img src="{{ episode.image.url }}" height="125px" width="300px" style="object-fit: cover;">
<button type="submit" name="editepisodeform">
Edit Episode
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ edit_e_form }}
</form>
</button>
{% endfor %}
Okay, so it turns out that formsets were the way to go after all. Thanks to Willem Van Onsem's answer, I decided to go that route after all and it worked like a charm.
A form can only edit one instance, but with formsets, I was able to not only edit each episode rather than just the first instance, but even create a new object and delete multiple objects at the same time!
views.py
#import the formset
from django.forms import modelformset_factory, inlineformset_factory
#formset
EpisodeFormset = inlineformset_factory(Series, Episode, fields=('name', 'episode_no', 'description', 'image'), can_delete=True, extra=1)
#post call
if request.method == 'POST':
if 'editepisodeform' in request.POST:
formset = EpisodeFormset(request.POST, request.FILES, instance=series, prefix='editepisode')
if formset.is_valid():
formset.save()
return redirect('series_detail', pk=pk)
else:
return redirect('series_detail', pk=pk)
else:
formset = EpisodeFormset(instance=series)
series_detail.html
<div>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<button type="submit" class="btn btn-primary" name="editepisodeform">Edit</button>
{{ form.as_p }}
{% for episode in episodes %}
{% if episode.episode_no == form.episode_no.value %}
Episode: {{ episode.episode_no }} <br>
Name: {{ episode.name }} <br>
<img src="{{ episode.image.url }}" height="125px" width="300px" style="object-fit: cover;"> <br> {% endif %}
{% endif %}
{% endfor %}
{% endfor %}
</form>
</div>

How do I use two lists in one Django for tag?

I have two letters that I would like to show in my template at the same time. How can I do it in the template the fastest and easiest way?
Where set_1 = A, B, C, D,
ser_2 = result from my django queryset
Is there something like the below? Any help will be appreciated
{% for b in set_1 and a in set_2 %}
<p>{{ b }} - {{ a }}</p>
{% endfor %}
EDIT:
How can I use this when my queryset returns a list of fields in my form, as in the view below?
views.py
def account(request):
data_now = datetime.datetime.now().strftime("%Y-%m-%d")
test = Time.objects.filter(day_time__day_name='Monday')
#my form
TimeFormSet = modelformset_factory(Time, fields=('free_or_no',), labels={'free_or_no': '*odznacz jeżeli zajęte',})
if request.method == "POST":
formset = TimeFormSet(
request.POST,
queryset=Time.objects.filter(day_time__day_name='Monday'),
)
if formset.is_valid():
formset.save()
return HttpResponseRedirect(reverse('app:account'))
else:
formset = TimeFormSet(queryset=Time.objects.filter(day_time__day_name='Monday'))
list_form = zip(formset, test)
context = {'data_now': data_now, 'time_edit_form': formset, 'test': test, 'list_form': list_form}
return render(request, 'account.html', context)
If I do everything as in your link, my browser returns an error:
IntegrityError at /account/
NOT NULL constraint failed: app_time.time_equivalent
html file
<form action="." method="post">
{% csrf_token %}
{{ time_edit_form.management_form }}
{% for item1, item2 in list_form %}
<p>{{item2}} {{item1}}</p>
{% endfor %}
<button type="submit" class="btn btn-block btn-primary"> ZapiszXXX</button>
</form>

Django: ModelFormSet saving first entry only

Update:
The issue seemed to be in the coding for Django-formset. I was processing it as an inline formset and not a model formset. The answer below was also correct. Thanks!
I am working with a model formset for an intermediate model. I am using django-formset js to add additional formset fields on the template. Most everything works OK except that when I go to save the formset only the first entry is being saved to the DB. The first entry is saved and assigned correctly but any after than just disappear. It is not throwing any errors so I am not sure what is going wrong. Thanks!
The Model
class StaffAssignment(models.Model):
study = models.ForeignKey(Study, related_name='study_set', null=True, on_delete=models.CASCADE)
staff = models.ForeignKey('account.UserProfile', related_name='assigned_to_set', null=True, on_delete=models.CASCADE)
role = models.CharField(max_length=100, null=True)
assigned_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-role',)
def __str__(self):
return '{} is assigned to {}'.format(self.staff, self.study)
The Form:
class AddStaff(forms.ModelForm):
model = StaffAssignment
fields = ('staff',)
def __init__(self, *args, **kwargs):
super(AddStaff, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs.update({'class': 'form-control'})
The View:
def add_staff(request, study_slug):
study = get_object_or_404(Study, slug=study_slug)
staff_formset = modelformset_factory(StaffAssignment, form=AddStaff, fields=('staff',), can_delete=True)
if request.method == 'POST':
staffList = staff_formset(request.POST, request.FILES)
if staffList.is_valid():
for assignment in staffList:
assigned = assignment.save(commit=False)
assigned.study = study
assigned.role = assigned.staff.job_title
assigned.save()
return HttpResponseRedirect(reverse('studies:studydashboard'))
else:
HttpResponse('Something is messed up')
else:
staffList = staff_formset(queryset=StaffAssignment.objects.none())
return render(request, 'studies/addstaff.html', {'staffList': staffList, 'study': study})
The Template:
<form action="{% url 'studies:addstaff' study.slug %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="box-body">
{% for list in staffList %}
<div class="form-group" id="formset">
{% if list.instance.pk %}{{ list.DELETE }}{% endif %}
{{ list.staff }}
{% if list.staff.errors %}
{% for error in list.staff.errors %}
{{ error|escape }}
{% endfor %}
{% endif %}
</div>
{% endfor %}
{{ staffList.management_form }}
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
You are not including the primary key field in the template, as required by the docs. Add
{% for list in staffList %}
{{ list.pk }}
...
{% endfor %}

Can i use a filtre in a template in django?

I made a loop allowing me to display for each page questions and answers utilisers.
I wonder if it's possible in the template to "filter" to get only the issues of page 1 for example?
<form action="" method="GET">
{{ form.as_p }}
{% for page in pages %}<hr>
{{ page }}:
<br>{% for reply in page.reply_set.all %}<br> {{ reply.question }} --> {{ reply.answer }} (Author : {{ reply.user }}) {% endfor %}
{% endfor %}
I have a fields ManytoMany so this is what it's hard to understand...
class Question(models.Model):
label = models.CharField(max_length=30)
def __str__(self):
return self.label
class Page(models.Model):
title = models.CharField(max_length=30)
def __str__(self):
return self.title
class Reply(models.Model):
page = models.ManyToManyField(Page)
question = models.ForeignKey(Question)
user = models.ForeignKey(Personne)
answer = models.CharField(max_length=30)
creationDate = models.DateTimeField(default=timezone.now(),blank=True, verbose_name="Date de création")
def __str__(self):
return str(self.answer)
You can see on this screenshot I have tree pages('name visit 1 visit 2 visit 3) and i want just the first visit what is the loop for get this ?
The best I would get is the page that I want based on the id
EDIT : views.py
def access(request,instance):
replies = Reply.objects.all()
questions = Question.objects.values()
logged_user = get_logged_user_from_request(request)
numPages = Page.objects.get(pk=instance)
pages = Page.objects.all()
# pagesfilter = Page.objects.get(pk=instance).reply_set.all()
form = ReplyForm(request.GET)
personnes = Personne.objects.all()
if logged_user:
if len(request.GET) > 0:
form = ReplyForm(request.GET)
if form.is_valid():
form.save(commit=True)
return HttpResponseRedirect('/reply')
else:
return render_to_response('polls/access.html', {'pagesfilter':pagesfilter, 'numPages': numPages, 'personnes': personnes, 'replies': replies, 'questions': questions,'pages':pages, 'form': form})
else:
form = ReplyForm()
return render_to_response('polls/access.html', {'pagesfilter':pagesfilter, 'numPages': numPages, 'personnes':personnes, 'replies': replies, 'questions': questions, 'pages':pages, 'form': form})
else:
return HttpResponseRedirect('/login')
If you know visit1 is the first element in the loop you can do:
{% for page in pages %}<hr>
{% if forloop.first %}
{{ page }}:
<br>{% for reply in page.reply_set.all %}<br> {{ reply.question }} --> {{ reply.answer }} (Author : {{ reply.user }}) {% endfor %}
{% endif %}
{% endfor %}
But the best way, I think, is to get visit1 in your view through a query and send it then to the template.
EDIT
As your view looks
numPages = Page.objects.get(pk=instance)
gets the Page you want. So, in template you could use it:
{{ numPage }}
{% for reply in numPage.reply_set.all %}
{{ reply.question }}--> {{ reply.answer }}(Author : {{ reply.user }})
{% endfor %}

django display ManyToManyField as a form field

I am trying to display a ManyToManyField in my template:
class GvtCompo(models.Model):
startDate=models.DateField(max_length=10, blank=False, null=False)
endDate=models.DateField(max_length=10, blank=False, null=False)
gvtCompo= models.CharField(max_length=1000, blank=False, null=False)
class Act(models.Model):
gvtCompo= models.ManyToManyField(GvtCompo)
In my view, I can display the object, no problem:
for gvtCompo in act.gvtCompo.all():
print gvtCompo.gvtCompo
In my template, I have a list of "GvtCompo Object" with this code (not nice):
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field }}
</div>
{% endfor %}
I have tried to make it nicer, but the following code just not work (nothing appears):
{% for field in form %}
{% if field.name == "gvtCompo" %}
{% for gvtCompo in field.gvtCompo.all %}
{{ gvtCompo.gvtCompo }}
{% endfor %}
{% endif %}
{% endfor %}
What's wrong?
*Edit: *
If I don't use the form but an instance of the model (act) passed to render_to_response it displays the ManyToManyField values
{% for gvtCompo in field.gvtCompo.all %}
changed to
{% for gvtCompo in act.gvtCompo.all %}
However there is not form field anymore, so it can't be modified, validated and saved!
You are skipping a step. You first need to create a form.
In forms.py you can create a ModelForm. A ModelForm is a form based on your model:
from django.forms import ModelForm
from myapp.models import Act
class ActForm(ModelForm):
class Meta:
model = Act
Then your view:
from myapp.models import Act
from myapp.forms import ActForm
def add_view(request):
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
form.save()
else:
form = ActForm() # Creating a empty form.
return render_to_response("template.html", {
"form": form,
})
def edit_view(request):
obj = Act.objects.get(pk=1)
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
form.save()
else:
form = ActForm(instance=obj) # Creating form pre-filled with obj.
return render_to_response("template.html", {
"form": form,
})
If you want to implement this situation more than once. DRY: https://docs.djangoproject.com/en/1.5/topics/class-based-views/intro/#handling-forms-with-class-based-views
In your template.html:
{{ form }}
Disclaimer: This code is not tested.
https://docs.djangoproject.com/en/1.5/ref/forms/
https://docs.djangoproject.com/en/1.5/topics/forms/modelforms/
Update:
You can pass multiple forms to one <form>...</form> in your template. So create two forms. One Act form (see above) and one GvtCompo formset. The formset contains all GvtCompo's that have a relation to Act.
from django.forms.models import modelformset_factory
act = Act.objects.get(pk=1) #The same act as you used to create the form above.
GvtFormSet = modelformset_factory(GvtCompo)
formset = GvtFormSet(queryset=act.gvtCompo.all())
Template can be:
<form ...>
{% for field in form %}
{% if field.name == "gvtCompo" %}
{{ formset }}
{% else %}
{{ field }}
{% endif %}
{% endfor %}
</form>
Note: If your form and formset have colliding field names use prefix="some_prefix":
Django - Working with multiple forms
When looping over the form, it should be:
{% for field in form %}
{% if field.name == "gvtCompo" %}
{% for gvtCompo in form.instance.gvtCompo.all %}
{{ gvtCompo.gvtCompo }}
{% endfor %}
{% endif %}
{% endfor %}
field itself has no related field.gvtCompo.