Django Model Form Set POSTs But Does Not Save - python

I have a Django site built on a model formset (forms.py). The model formset is picked up by my view (views.py). The view is rendered in my template (alerts.html). The user sees a list of alerts with entity and logic populated. They must enter a comment on one or more forms in the formset and then click the submit button to post the one or more forms to the DB. When the submit button is clicked currently, the page refreshes and a successful POST (200) displays in runserver, but the data is not saved to the DB. formset.errors shows that comment is required for each field, not just the form that was changed.
I tried adding if formset.has_changed(): prior to calling formset.save(), but the problem persisted.
How should I alter my project to allow the model formset to be saved appropriately?
EDIT: I migrated blank=True for comment. Now when the submit button is clicked, the data is saved. However, the comment text (and the rest of the form) remains in the table in the template. When submit is clicked again, the comment text remains and entity and logic are replaced by blanks.
forms.py
class AlertForm(ModelForm):
class Meta:
model = Alert
fields = [
'comment'
]
AlertFormSet = modelformset_factory(Alert, extra=0, form=AlertForm)
views.py
def alerts(request):
newAlerts = Alert.objects.filter(comment='')
formset = AlertFormSet(request.POST or None, queryset=newAlerts)
context = {'formset':formset}
if request.method == 'POST':
formset = formset
if formset.is_valid():
formset.save()
else:
formset = formset
print(formset.errors)
return render(request, 'alerts/alerts.html', context)
alerts.html
<form method='POST' action=''>
{{ formset.management_form }}
{% csrf_token %}
<input name="submit" value="Submit" id="submit-id-submit" type="submit">
{% for form in formset %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% endfor %}
<table>
<thead>
<tr>
<th>Entity</th>
<th>Logic</th>
<th>Comment</th>
</tr>
</thead>
<tbody>
{% for form in formset %}
<tr>
<td>{{ form.instance.entity }}</td>
<td>{{ form.instance.logic }}</td>
<td>{{ form.comment }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>

The formset is invalid because you are not submitting the values for the entity or logic fields. You would see this if you printed formset.errors in your view or included the form errors in the template.
Since you don't want entity or logic to be editable, you should not include these in the formset's fields:
class AlertForm(ModelForm):
class Meta:
model = Alert
fields = [
'comment',
]
Since you are defining fields in the form, you shouldn't need to include exclude when you call modelformset_factory.
AlertFormSet = modelformset_factory(Alert, extra=0, form=AlertForm)

Try looping over the data in the formset like this
if request.method == 'POST':
formset = formset
if formset.is_valid():
for form in formset:
cleaned_data = form.cleaned_data
entity = cleaned_data.get('entity')
logic = cleaned_data.get('logic')
comment = cleaned_data.get('comment')
# create a new Alert object here
alert = Alert(entity=entity, logic=logic, comment=comment)
alert.save()

Related

Django restricting users from updating/deleting other users posts

views.py
#login_required(login_url='/login')
def updatepost(request,pk):
post = Post.objects.get(id=pk)
form = PostForm(instance=post)
if request.method =='POST':
form = PostForm(request.POST, request.FILES, instance=post)
if form.is_valid():
form.save()
return redirect ('mainpage')
context = {'form':form}
return render( request, 'postform.html', context )
postform.html
{% include 'main.html'%}
{% block content %}
{%if user.is_authenticated%}
{% if user.id == post.user.id%}
<div>
<form method="POST" action ="">
{% csrf_token %}
{{form.as_p}}
<input type="Submit" value ="Submit"/>
</form>
</div>
{%endif%}
{%endif%}
{% endblock content %}
I am trying to restrict user who is logged in - from updating , deleting other users posts.
However when I Try to use {% if user.id == post.user.id%} , the page becomes blank even for the user who is editing his own post. The same goes for deleting.
It works on mainpages - where posts are displayed (it hides edit and delete buttons).
What is the reason that the post is not showing inside the template ?
I don't understand that even {{post.user}} in postform.html does not appear , neither on deleteform etc. - why this data of objects of a post is not being sent to postform.html ?
You should change the context to access the post instance from your template.
context = {'form': form, 'post': post}
You can only access the context values from the templates. So pass the ones you want to use while you are returning a response.

How do i save/show manytomany field in select option field

I am working on a project where users can upload a post, edit a post and tag other users. I was able to implement upload post, edit and tag users (I implemented tag users with jquery ui). When i want to edit a post i have already tagged users on, i do not get the names of the users on select option field, instead it shows empty. How do i save/show users already tagged users from manytomany field in select option field in template. I will attach images to my question for clarity.
These are the users i tagged on my post, showing in manytomany field:
This image below is what i want, when i want to edit my post, let all tagged users of that post be shown in select option field:
Views.py:
def update_post_view(request, id):
#update post
post = get_object_or_404(Post, id=id)
edit_form = PostForm(request.POST, instance=post)
if request.method == "POST":
if edit_form.is_valid():
edit_form.save()
return redirect('site:comments', id=id)
else:
edit_form = PostForm(instance=post)
#Tag username dropdown in form
tag_list_users = User.objects.all()
context = {
'edit_form': edit_form,
'post':post,
'tag_list_users': tag_list_users,
}
return render(request, 'update_post.html', context)
Template:
<label for="id_tag_someone">Tag someone:</label><br>
<select name="tag_someone" id="id_tag_someone" multiple="" class="chosen w-100">
{% for user in tag_list_users %}
<option value="{{user.pk}}">{{user}}</option>
{% endfor %}
</select>
jQuery:
$('.chosen').chosen();
I was able to get this working by adding code to my views and template:
Views.py
#tagged_users is a related_name to tag someone in Model
tagged_user = User.objects.filter(tagged_users=post)
Template
<label for="id_tag_someone">Tag someone:</label><br>
<select name="tag_someone" id="id_tag_someone" multiple="" class="chosen w-100">
{% for user in tagged_users %}
<option value="{{user.pk}}">{{user}}</option>
{% endfor %}
{% for user in tag_list_users %}
<option value="{{user.pk}}">{{user}}</option>
{% endfor %}
</select>

Maintaining static files

In my django framework I collect user input from a form and create a matplotlib image. This image is displayed after a button push in a tagged field in an html file.
After each button push this image is updated, changes and is newly displayed. This works well. Now I intend to introduce a second image at a second tag, that shows up and changes after pressing a different button.
The problem that I have, is that after pushing the second button, the first image disappears and after pushing the first button, the second image disappears. Here is part of the html code:
<form method=post action="">
{% csrf_token %}
<table>
{% for field in form %}
<tr>
<td>{{ field.label }}</td>
<td>{{ field }}</td>
</tr>
{% endfor %}
</table>
<br>
<p><input type=submit name='button1' value='Create' class="btn btn-default"></form></p>
</form>
<p>
{% if result != None %}
{% load static %}
<img src="{% get_static_prefix %}{{ result }}" width=1000>
{% endif %}
</p>
<form method="post" class="topright">
{{form3}}
{% csrf_token %}
<p><input type=submit name='button2' value='Update' class="btn btn-default" default=1></p>
</form>
<form method=post action="">
{% csrf_token %}
<table>
{% for field in form4 %}
<tr>
<td>{{ field.label }}</td>
<td>{{ field }}</td>
</tr>
{% endfor %}
</table>
<p><input type=submit name='button3' value='Predict' class="btn btn-default" default=1></p>
</form>
<p>
{% if predictresult != None %}
{% load static %}
<img src="{% get_static_prefix %}{{ predictresult }}" width=1000>
{% endif %}
</p>
The image files are saved via: plt.savefig(plotfile) into the static folder. Most of the views code is the following:
def bayes_create_and_update(request):
#os.chdir(os.path.dirname(__file__))
global bandit
global invgammaresult
global predictresult
invgammaresult = None
predictresult = None
if request.method == 'POST' and 'button1' in request.POST:
form = InputForm(request.POST)
if form.is_valid():
form2 = form.save(commit=False)
bandit = gauss_bandit(form2.m, form2.s)
invgammaresult=pull_and_update(bandit,1)
invgammaresult=invgammaresult[7:]
form3= Sample_InputForm()
form4=Predict_InputForm()
elif request.method == 'POST' and 'button2' in request.POST:
form3 = Sample_InputForm(request.POST)
if form3.is_valid():
form2 = form3.save(commit=False)
invgammaresult=pull_and_update(bandit,form2.n)
invgammaresult=invgammaresult[7:]
form = InputForm(request.POST)
form4=Predict_InputForm()
elif request.method == 'POST' and 'button3' in request.POST:
form4 = Predict_InputForm(request.POST)
if form4.is_valid():
form2 = form4.save(commit=False)
predictresult=bandit.predict(form2.p1,form2.p2)
predictresult=predictresult[7:]
form = InputForm(request.POST)
form3= Sample_InputForm()
else:
form = InputForm()
form3= Sample_InputForm()
form4=Predict_InputForm
return render ( request, 'C:/Users/Jan/PycharmProjects/Newversion/newattempt/online_comp/templates/bayes/bayes.html',
{'form': form,
'result': invgammaresult,
'form3':form3,
'form4':form4,
'predictresult':predictresult
})
and my models that give rise to the form is this code:
from django.db import models
from django.forms import ModelForm
class Input(models.Model):
m = models.FloatField(
verbose_name=' Mean of the Normal Distribution:', default=0.0)
s = models.FloatField(
verbose_name=' Standard Deviation of the Normal Distribution:', default=1.0)
class InputForm(ModelForm):
class Meta:
model = Input
fields='__all__'
class Sample_Input(models.Model):
n = models.FloatField(
verbose_name=' Number of samples you wish to draw:', default=1)
class Sample_InputForm(ModelForm):
class Meta:
model=Sample_Input
fields='__all__'
class Predict_Input(models.Model):
p1 = models.FloatField(
verbose_name=' Lower bound', default=-1.0)
p2 = models.FloatField(
verbose_name=' Upper bound', default=1.0)
class Predict_InputForm(ModelForm):
class Meta:
model=Predict_Input
fields='__all__'
Thx
I think it would be helpful if you posted your view as well as the source for the forms you are putting into your html. One possibility is that you have the request.POST info switched in the views but would need to see.
Small detail but form method=post should be form method="post" everywhere correct?
I have solved the problem by creating two sub directories in the static folder and changing the path of saving the file in the compute file (not shown above). So the image output of both functions wouldn't interfere with each other.

django ModelForm save issue with ManyToManyField

I'm new to Django and was needing some help on a view error i am getting.
I wrote a view that will display a data table of "Groups" if the request method is GET, or display a form to edit a particular "Group" if the request method is POST (item to edit is passed with POST data).
Also, if POST on an existing item, i'd like the form to be pre-populated with the data i already have in the table for that item. I've pretty much got it all down, except when i goto save an edited form, i keep getting this error:
"Cannot set values on a ManyToManyField which specifies an intermediary model"
Any help would be greatly appreciated. Also, I'm new to all this web dev stuff, so if i'm doing something completely silly or am missing a concept, please feel free to flame me accordingly. ;-)
Model
class Alias(models.Model):
def __unicode__(self):
return unicode(self.alias)
alias = models.CharField(max_length=32)
class Octet(models.Model):
def __unicode__(self):
return unicode(self.num)
num = models.IntegerField(max_length=3)
class Group(models.Model):
def __unicode__(self):
return unicode(self.name)
name = models.CharField(max_length=32) #name of the group
id = models.AutoField(primary_key=True) #primary key
octets = models.ManyToManyField(Octet, through='OctetAssignment', blank=True) #not required
aliases = models.ManyToManyField(Alias, through='AliasAssignment', blank=True) #not required
class OctetAssignment(models.Model):
octet = models.ForeignKey(Octet) #octet
group = models.ForeignKey(Group) #group that octet is assigned to
class AliasAssignment(models.Model):
alias = models.ForeignKey(Alias)
group = models.ForeignKey(Group)
View
def index(request):
if request.method == 'GET':
groups = Group.objects.all().order_by('name')
return render_to_response('groups.html',
{ 'groups': groups, },
context_instance = RequestContext(request),
)
elif request.method == "POST":
g = Group.objects.get(id=request.POST['id'])
form = GroupEditForm(instance=g)
return render_to_response('groups.html',
{ 'form': form, },
context_instance = RequestContext(request),
)
def save(request):
if request.method == "POST":
form = GroupEditForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/tradedesk/groups/') # Redirect after POST
To make it complete, here is the form template code i'm using that renders the table and edit page.
Template
<h1>Group Information</h1>
{% if groups %}
<table border=1>
{% for group in groups %}
<tr>
<td>{{group.name}}</td>
<td>{% for octet in group.octets.all %} {{octet}} {% endfor %}</td>
<td>{% for alias in group.aliases.all %} {{alias}} {% endfor %}</td>
<td>{{group.analyst}}</td>
</tr>
{% endfor %}
</table>
<br></br>
<form method="post" action="/groups/">{% csrf_token %}
<select name="id" >
{% for group in groups %}
<option value="{{group.id}}">{{group.name}}</option>
{% endfor %}
</select>
<input type="submit" value="Edit">
</form>
{% endif %}
{% if form %}
<form method="post" action="/groups/save/">{% csrf_token %}
{{form}}
<br></br>
<input type="submit" value="Save">
<form>
{% endif %}
</div>
Try to remove the intermediary models OctetAssignment and AliasAssignment. They should be used only when you want to add custom fields to them. Otherwise Django creates them and uses them transparently by itself.

Management form error while using modelformsets ('ManagementForm data is missing or has been tampered with')

I have a models.py class as below
class Educational_Qualification(models.Model):
user = models.ForeignKey(User)
exam = models.CharField(max_length=40)
pass_month = models.CharField(max_length=40)
I have a views.py as below
def create_qualification(request):
QFormSet = modelformset_factory(Educational_Qualification, extra=3, exclude=("user",))
if request.method == "POST":
formset = QFormSet(request.POST, request.FILES)
if formset.is_valid():
formset.save()
for form in formset.forms:
if form.is_valid():
quali= form.save(commit=False)
quali.user = request.user
quali.save()
return HttpResponse("Saved")
else:
return HttpResponse("Snafu")
else:
formset = QFormSet()
return render_to_response("register/edu.html", {"formset":formset}, context_instance=RequestContext(request))
When I submit the form, it throws up the validation Error. stating that ManagementForm data is missing or has been tampered with'
I have formset.management_form in my template.
What could be the issue?
The error is not in your views or the models, but in the templates.
The right way to render the formset, is:
<form method="post" action="">
<table>
{{ formset }}
</table>
</form>
or
<form method="post" action="">
{{ formset.management_form }}
<table>
{% for form in formset.forms %}
{{ form }}
{% endfor %}
</table>
</form>
I guess, you are looping over the forms in the templates without including the management form?
It also happens if there are multiple views involved and one of them is not aware of the formset prefix.
Get view:
def someview(request):
...
formset = Formset(prefix="foo")
...
Post view (Potentially an Ajax form submit handler):
def ajaxview(request):
...
formset = Formset(request.POST, prefix="foo") # here
...

Categories

Resources