I am stuck with a problem - I want to add a simple form field to edit the objects that I am looping through in the template. Here's my model:
class Topic(BaseModel):
name = models.TextField()
Here's my model form:
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ["name"]
And here's my views:
def App(request):
tname = Topic.objects.get(pk=1)
if request.method == "POST":
form = TopicForm(data = request.POST, instance=tname)
if form.is_valid():
form.save()
And my template is simple:
{% for lecture in lectures %}
<form action="/app/" method="POST">
{% csrf_token %}
{{ form }}
<input type="submit" value="Post">
</form>
{% endfor %}
The thing is that I want to have a form field to edit EACH model not just one that has a pk of 1... how do I do that ?
I think you need to do objects.all() instead of get(pk=1). Then loop over those objects, and save them to a list that you save to the context. Something like this:
tnames = Topic.objects.all()
lectures = []
for tname in tnames:
lectures.append(TopicForm(instance=tname))
context = {
'lectures' : lectures
}
More specifically, you probably want to look into Model formsets. See https://docs.djangoproject.com/en/1.6/topics/forms/modelforms/#changing-the-queryset. Then you can directly give a queryset as initial data.
Related
I'm using formsets to pass several modelforms with existing model instances to a template. I'd like to do something in the template for each form depending on the value of a model field (let's call it field_check) for that instance. Field_check is not intended for user input and a required field.
If I include field_check into my form without rendering it, I can easily access its value. But then after submitting the form Django complains that field_check is required.
What is the recommended way to access the value of field_check without including it as an input field or running into the required field problem?
Models.py
class MyModel(models.Model):
field_check = models.CharField(max_length=50)
user_input_field = models.CharField(max_length=50)
some_type = models.CharField(max_length=50)
Forms.py
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = [
'user_input_field',
'field_check', #having this field here creates problems, since it's not intended for user input
]
MyFormSet = forms.modelformset_factory(MyModel, form = MyModelForm, extra=0)
Views.py
def myview(request):
this_type = 1
formset = MyFormSet(request.POST or None, queryset = MyModel.objects.filter(some_type = this_type))
if request.method == 'POST':
if formset.is_valid():
formset.save()
return render(request, 'some_other_template.html')
context = {'formset':formset}
return render(request, 'mytemplate.html', context)
Template
<form method="post">
{{ formset.management_form }}
{% for form in formset %}
{{form.id}
# here is the part where I check for the value of field_check
{% if form.field_check == 'Some value' %}
Some text
{% else %}
Some other text
{% endif %}
{{form.user_input_field}}
{% endfor %}
<button type="submit">
</form>
I am having some issues with a (model)form consisting of just a single button. When I try to submit the form this message is displayed:
user
This field is required.
The ModelForm looks like this:
from django.forms import ModelForm
from .models import HulpOproep
class HulpOproepForm(ModelForm):
class Meta:
model = HulpOproep
fields = ['user', ]
The Model looks like this:
class HulpOproep(models.Model):
user = models.ForeignKey(User)
time = models.DateTimeField(auto_now_add=True, verbose_name='Tijd')
def __str__(self):
return '%s %s' % (self.user.username, str(self.time))
def username(self):
return self.user.username
def first_name(self):
return self.user.first_name
def last_name(self):
return self.user.last_name
class Meta:
verbose_name = 'Hulp Oproep'
verbose_name_plural = 'Hulp Oproepen'
The View looks like this:
def verzend_oproep(request):
if request.method == 'POST':
form = HulpOproepForm(request.POST)
if form.is_valid():
oproep = form.save(commit=False)
oproep.user = request.user
oproep.save()
return redirect('portal/index/')
else:
form = HulpOproepForm()
return render(request, 'portal/verzend_oproep.html', {'form': form})
The Template:
{% extends "base.html" %}
{% block head %}
<title>Zorggroep | Hulp Oproep</title>
{% endblock %}
{% block body%}
<h1>Verstuur Hulpoproep</h1>
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_P }}
{{ form.errors }}
<button type="submit" class="save btn btn-default">Verstuur</button>
</form>
{% endblock %}
The 'user' in the HulpOproep model is a ForeignKey and should be the currently logged in user's User object. I tried to specify this using the line:
oproep.user = request.user
So what should happen is:
Get the current user's 'User' object and use it as the 'HulpOproepForm.user'. This way the 'HulpOproepForm.user' is the 'HulpOproep.user' and a Foreign Key.
I have followed multiple tutorials and have searched around, but I cannot find a solution. I'm sorry if the answer is logical, but I have been using Django for only 5 days and have 1.5 months of programming experience under my belt.
Thank you!
Thanks PatNowak and Radek!
I did not know the form was waiting for user input instead of code input. I managed to fix it by adding exclude to the ModelForm.
class HulpOproepForm(ModelForm):
class Meta:
model = HulpOproep
exclude = ['user', 'time']
ok i am following the formset. Pretty much i understand that formset is for multiple form.
so this example i just want take four values same time but the ouput in html have only one form is showing.
Shall i want to make extra filed like this <input id="your_name" type="text" name="your_name"> or django will do the rest or any other way to do that.?
models.py
class Article(models.Model):
title = models.CharField(max_length=100)
pub_date = models.DateField(auto_now_add=True)
forms.py
class ArticleForm(forms.Form):
title = forms.CharField()
#pub_date = forms.DateField()
ArticleFormSet = formset_factory(ArticleForm, extra=4, validate_max=True)
views.py
def book(request):
if request.method == 'POST':
formset = ArticleForm(request.POST)
if formset.is_valid():
new = Article()
new.title = request.POST.get('title', None)
#new.pub_date = request.POST.get('pub_date', None)
new.save()
return HttpResponseRedirect(reverse('firstapp.views.book'))
else:
formset = ArticleForm()
return render_to_response('get.html',{'formset': formset}, context_instance = RequestContext(request))
And the html look like this
<form method="post" action="">
{% csrf_token %}
{{ formset.management_form }}
<table>
{% for form in formset %}
{{ form }}
{% endfor %}
</table>
<input type="submit"/>
</form>
In your view you are binding formset to an ArticleForm, not to an ArticleFormSet. Also you are only creating one single Article from it, and you're not even using the form properly (ie: you're getting the title directly from request.POST instead of getting it from your form's cleaned_data). Your view code should look something like this (caveat: untested and possibly buggy code, but at least you'll get the picture).
def book(request):
if request.method == 'POST':
formset = ArticleFormSet(request.POST)
if formset.is_valid():
for data in formset.cleaned_data:
Article.objects.create(title=data['title'])
return HttpResponseRedirect(reverse('firstapp.views.book'))
else:
formset = ArticleFormSet()
return render_to_response('get.html',{'formset': formset},
context_instance = RequestContext(request))
As a last point, I strongly suggest you have a look at ModelForms.
Sorry the title isn't very descriptive. The context: I have an event full of participants. When the event is over I want to leave feedback for all of the other participants.
models.py
class Feedback(models.Model):
action = models.ForeignKey(Action)
feedback_by = models.ForeignKey(UserProfile, related_name='feedback_by')
feedback_for = models.ForeignKey(UserProfile, related_name='feedback_for')
comment = models.CharField(max_length=200)
no_show = models.BooleanField()
created = models.DateTimeField()
modified = models.DateTimeField()
forms.py
class FeedbackFormSet(BaseModelFormSet):
def add_fields(self, form, index):
super(FeedbackFormSet, self).add_fields(form, index)
form.fields['is_checked'] = forms.BooleanField(required=False)
class FeedbackForm(forms.ModelForm):
comment = forms.CharField(label=(u"Comment"), widget=forms.Textarea())
class Meta:
model = Feedback
fields = ['comment', 'no_show']
I want to create a feedback page, where there would be one instance of the FeedbackForm for each participant. After some searching it seems that to do that I want to be using a FormSet, but I'm not finding the documentation for it very helpful and I can't seem to find any good examples.
If a formset is the way to go, could you guys help me out with some (view/formset basically) starter code? If not, can you point me to what I should be doing? Thanks.
EDIT: I've added my view and template code below.
views.py
#login_required
def new_feedback(request, action_id):
action = get_object_or_404(Action, id=action_id)
profile = UserProfile.objects.get(user_id=request.user.id)
participants = all_info_many_profiles(action.participants.filter(~Q(id=profile.id)))
fbformset = modelformset_factory(Feedback, form=FeedbackForm, formset=FeedbackFormSet)
if request.method == 'POST':
formset = fbformset(request.POST, request.FILES, queryset=action.participants.filter(~Q(id=profile.id)))
if formset.is_valid():
formset.save()
else:
print formset.errors
else:
formset = fbformset(queryset=action.participants.filter(~Q(id=profile.id)))
return render(request, 'app/new_feedback.html',
{'action': action, 'participants': participants, 'formset': formset}
new_feedback.html
{% block body_block %}
<h1>Leave Feedback</h1>
{% for participant in participants %}
<li>{{ participant.username }}</li>
{% endfor %}
<form method="post" action="">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form.as_p }} <br />
{% endfor %}
<input type="submit" name="submit" value="Submit Feedback" />
</form>
{% endblock %}
What I'm trying to achieve is to associate one form to each user in the list (or queryset) of participants. What I currently have shows one more form than I want (for example, when I list each user I'll see 3 names but 4 forms example) and I don't know if or how the users/forms are related.
The idea is that the feedback_for field will get its value automatically, ie in the view I would do:
if formset.is_valid():
for form in formset:
a = form.save(commit=false)
a.feedback = participant
a.save()
On top of that, I added an extra field "is_checked" which is intended to specify whether I'm leaving feedback for that user or not. Example of full functionality:
user1 [X] is_checked
... rest of form
user2 [] is_checked
... rest of form
user3 [X] is_checked
... rest of form
Then when I hit submit it will create two new entries in the Feedback table, one for user1 and one for user3
I am getting this error when i visit my page:
Caught AttributeError while rendering: 'WSGIRequest' object has no attribute 'get'
The error kicks in on line "17" of my html, which is the line that outputs form.as_p
The html looks like this:
{% extends "base.htm" %}
{% block content %}
{% if story_list %}
{% for story in story_list %}
<div class="Story">
{{ story.title }}
</div>
{% endfor %}
{% else %}
<p>No stories are present - enter one below</p>
{% endif %}
<h3>Create a new story</h3>
<form action="/addStory" method="post">
{% csrf_token %}
{{ form.as_p }} ***THIS IS LINE 17***
<input type="submit" value="Submit"/>
</form>
{% endblock %}
The problem is i have a view that does two things, and from the django tutorials overrode the get_context_data method to add the second item to the django context. Because, um, that's what i'm meant to do, riiiiiiiiight?
#for showing of the stories!
class StoryShowView(ListView):
model = StoryForm
def get_queryset(self):
return getStoryItemsForUser(self.request)
def get_context_data(self, **kwargs):
context = super(StoryShowView,self).get_context_data(**kwargs)
context['form'] = createNewStoryForm(self.request)
return context
Where, well, the method createNewStoryForm just does this:
def createNewStoryForm(request):
return StoryForm(request)
and StoryForm is just this:
class StoryForm(ModelForm):
class Meta:
model = Story
ordering = ['create_date']
and the Story model is a normal model, that probably isn't part of the problem, but, hey, i am a cutting and a pasting, so here goes!
class Story(models.Model):
user = models.ForeignKey(User)
post = models.ForeignKey(Post)
title = models.CharField(max_length=100)
is_closed = models.BooleanField()
is_random = models.BooleanField() # for uncategorised stories. Only one of these.
result = models.CharField(max_length=20) #how did the relo work out?
create_date = models.DateTimeField('date created')
def __unicode__(self):
return self.title
Any ideas what i am doing wrong?
UPDATE:
ah, it was the line::
return StoryForm(request)
I take it i can either pass in a "request.POST" or nothing, is that it?
Probably you're right and you were passing request instead of request.POST, reqest.GET or request.REQUEST to the constructor of your form. See the doc on how to use forms:
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
Two problems that I can see. The easy one being that you can simply replace this line:
context['form'] = createNewStoryForm(self.request)
with
context['form'] = StoryForm(request.POST, request.FILES)
Finally shouldn't this:
class StoryShowView(ListView):
model = StoryForm
Be:
class StoryShowView(ListView):
model = Story