When creating a form bounded to a model in django, it does not render at all.
I have the following model:
class Recipe(models.Model):
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
url = models.TextField(validators=[URLValidator()], blank = True)
photo = models.ImageField(upload_to='photos', blank = True)
def publish(self):
self.created_date = timezone.now()
self.save()
def __str__(self):
return self.title
The form:
class RecipeForm(forms.ModelForm):
class Meta:
model = Recipe
fields = ['title', 'text']
The view:
def recipe_new(request):
form = RecipeForm()
return render(request, 'recipe_edit.html', {'form:': form})
The template:
{% extends 'base.html' %}
{% block content %}
<h1>New recipe</h1>
<form method="post" class="recipe-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
{% endblock %}
But only the title "New recipe" and "Save" button is rendered. If I try to print the form variable in my terminal, it prints out correctly. However, the response to the request always comes without the input fields (whether I use form.as_p or just form). Am I passing the form to the template incorrectly or is the form itself wrong?
You wrote:
{'form:': form}
notice the colon (:) in boldface in the key (you write the colon twice, once in the key, and once as a key-value separator). As a result you pass a variable with a name that has a colon in it. Changing the template might help, but I would strongly advice against it, since such characters have typically a specific meaning for template filters, etc. I would therefore advice to only use valid Python identifiers.
Since the name of the variable was form:, using form in the template did not make any sense, since - according to the Django render engine - that variable does not exists, so it resolves it with the string_if_invalid setting of the render engine (by default the empty string).
It should be:
def recipe_new(request):
form = RecipeForm()
return render(request, 'recipe_edit.html', {'form': form})
Related
I am trying to display a ModelForm with prepopulated instance data.
It works fine except for the ChoiceField which always displays the first choice given in forms.py ('LP') rather than the choice provided by the instance.
View:
def review(request):
order = Order.objects.get(user__pk=request.user.id)
form = ProjectReviewForm(instance=order)
context = {
'form': form,
}
return render(request, 'users/projectreview.html', context)
Forms:
class ReviewForm(forms.ModelForm):
LAND = 'LP' // #INSTANCE ALWAYS SHOWS THIS RATHER THAN INSTANCE DATA
DATA = 'DC'
STATIC = 'SW'
CHOICES = (
(LAND, 'LP'),
(DATA, 'DC'),
(STATIC, 'SW')
)
product = forms.ChoiceField(choices=CHOICES, widget=forms.Select(attrs={'class': 'form-field w-input'}),)
class Meta:
model = Order
fields = '__all__'
template:
<form method="POST" class="contact-form">
{% csrf_token %}
<h2 class="form-heading-small">Please make sure everything you've submitted is correct.</h2>
{{ form }}
<button type="submit" data-wait="Please wait..." class="button w-button">Looks good!</button>
</form>
The product field on the form is overriding the field on the model. Look into using a ModelChoiceField
I am trying to delete a record
My views.py file is
def AnnouncementDelete(request, pk):
announcement = get_object_or_404(Announcement, pk=pk)
if request.method=='POST':
announcement.delete()
return redirect('/')
return render(request, 'classroom/announcement_confirm_delete.html')
and my html file is
{% extends "classroom/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<form action="{% url 'classroom:AnnouncementDelete' announcement.id %}" method="post">
{% csrf_token %}
<input type="submit" value="Delete cat">
</form>
{% endblock content%}
my url.py file has the pattern
url(r'^delete/(?P<pk>[0-9]+)/$', views.AnnouncementDelete, name='AnnouncementDelete'),
and the model from where I want to delete the record is
class Announcement(models.Model):
title = models.CharField(max_length=30)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE)
def __str__(self):
return self.title
when I am trying to access http://127.0.0.1:8000/classroom/delete/1/
it is giving me the following error
NoReverseMatch at /classroom/delete/1/
Reverse for 'AnnouncementDelete' with arguments '('',)' not found. 1 pattern(s) tried: ['classroom/delete/(?P<pk>[0-9]+)/$']
Also i am a beginner to django and not very familiar with url(r'^delete/(?P<pk>[0-9]+)/$', views.AnnouncementDelete, name='AnnouncementDelete'), way. I generally use the ```path`` way.
EDIT
This is the view for uploading assignment
#login_required
def upload_announcement(request):
if(request.user.is_teacher==False):
return HttpResponse("This forms requires teacher previlodge")
else:
assignment_uploaded = False
teacher = request.user.Teacher
if request.method== 'POST':
form = AnnouncementForm(request.POST)
if form.is_valid():
upload = form.save(commit=False)
upload.teacher = teacher
upload.save()
assignment_uploaded = True
else:
form = AnnouncementForm()
return render(request, 'classroom/announcement_form.html', {'form':form, 'assignment_uploaded':assignment_uploaded})
This is the view for displaying all assignments
class AnnouncementListView(ListView):
context = {
'announcements' : Announcement.objects.all()
}
model = Announcement
template_name = 'classroom/all_announcements.html' #<app>/<model>_<viewtype>.html
context_object_name = 'announcements'
You can discard the url() line and use the path() variant for this like that:
path("delete/<int:pk>/", view=views.AnnouncementDelete, name="AnnouncementDelete"),
If that doesn't solve your problem, please report back with the view that renders the delete page.
In your html template you are using a variable (announcement) that you never pass from your view.
If i am thinking of this right, you should change your view to:
def AnnouncementDelete(request, pk):
announcement = get_object_or_404(Announcement, pk=pk)
if request.method=='POST':
announcement.delete()
return redirect('/')
return render(request, 'classroom/announcement_confirm_delete.html', {'announcement': announcement})
# this last guy over here sends the desired object to the template for rendering.
You did't ask for this but...
Since you mention in your question that you use path most of the times, i assume that you just wanted to test url.
Regarding the proper use of url variant check out this answer.
I have pretty complicated form items ;)
Here's my view for serving my form:
def view(request):
if request.method == "POST":
form = forms.ProfileEditForm(request.POST)
else:
form = forms.ProfileEditForm(initial={'country': request.user.profile.country})
return direct_to_template(request, "name/of/template.html",
{"form": form, "countries": Country.objects.all()})
Here's what the country model looks like:
class Country(models.Model):
iso2 = models.CharField()
iso3 = models.CharField()
name = models.CharField()
Here's what my form's clean() method looks like (highly simplified):
def clean(self):
self.cleaned_data['country'] = Country.objects.get(iso2=self.cleaned_data['country'])
return self.cleaned_data
Here's how I'm rendering it in a template:
<select name="country"{% if form.country.value %} data-initialvalue="{{form.country.value.iso2}}"{% endif %}>
{% for country in countries %}
<option value="{{country.iso2}}"{% if country.iso2==form.country.value.iso2 %} selected{% endif %}>{{country.name}}</option>
{% endfor %}
</select>
However, I'm noticing that what actually ends up being delivered to my template isn't a Country object, but a string of my input data, such as "US". When I initially render my template with the initial data, things look fine, but when validation fails, things get messed up. What should I do, and what am I doing wrong?
Just use ModelChoiceField for your country field.
And don't forget to define __unicode__ method of the model.
I am building a webapp which will be used by a company to carry out their daily operations. Things like sending invoices, tracking accounts receivable, tracking inventory (and therefore products). I have several models set up in my various apps to handle the different parts of the web-app. I will also be setting up permissions so that managers can edit more fields than, say, an office assistant.
This brings me to my question. How can I show all fields of a model and have some that can be edited and some that cannot be edited, and still save the model instance?
For example, I have a systems model for tracking systems (we install irrigation systems). The system ID is the primary key, and it is important for the user to see. However, they cannot change that ID since it would mess things up. Now, I have a view for displaying my models via a form using the "form.as_table". This is efficient, but merely spits out all the model fields with input fields filling in the values stored for that model instance. This includes the systemID field which should not be editable.
Because I don't want the user to edit the systemID field, I tried making it just a label within the html form, but django complains. Here's some code:
my model (not all of it, but some of it):
class System(models.Model):
systemID = models.CharField(max_length=10, primary_key=True, verbose_name = 'System ID')
systemOwner = models.ForeignKey (System_Owner)
installDate = models.DateField()
projectManager = models.ForeignKey(Employee, blank=True, null=True)
#more fields....
Then, my view for a specific model instance:
def system_details(request, systemID):
if request.method == 'POST':
sysEdit = System.objects.get(pk=systemID)
form = System_Form(request.POST, instance=sysEdit)
if form.is_valid():
form.save()
return HttpResponseRedirect('/systems/')
else:
sysView = System.objects.get(pk=systemID)
form = System_Form(instance=sysView)
return render_to_response('pages/systems/system_details.html', {'form': form}, context_instance=RequestContext(request))
Now the html page which displays the form:
<form action="" method="POST">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Save Changes">
<input type="button" value="Cancel Changes" onclick="window.location.href='/systems/'">
</form>
So, what I am thinking of doing is having two functions for the html. One is a form for displaying only those fields the user can edit, and the other is for just displaying the content of the field (the systemID). Then, in the view, when I want to save the changes the user made, I would do:
sysValues = System.objects.get(pk=SystemID)
form.save(commit = false)
form.pk = sysValues.sysValues.pk (or whatever the code is to assign the sysValues.pk to form.pk)
Is there an easier way to do this or would this be the best?
Thanks
One thing you can do is exclude the field you don't need in your form:
class System_Form(forms.ModelForm):
class Meta:
exclude = ('systemID',)
The other is to use read-only fields: http://docs.djangoproject.com/en/1.3/ref/contrib/admin/#django.contrib.admin.ModelAdmin.readonly_fields as #DTing suggessted
To make a field read only you can set the widget readonly attribute to True.
using your example:
class System_Form(ModelForm):
def __init__(self, *args, **kwargs):
super(System_Form, self).__init__(*args, **kwargs)
self.fields['systemID'].widget.attrs['readonly'] = True
class Meta:
model = System
or exclude the fields using exclude or fields in the class Meta of your form and display it in your template if desired like so:
forms.py
class System_Form(ModelForms):
class Meta:
model = System
exclude = ('systemID',)
views.py
def some_view(request, system_id):
system = System.objects.get(pk=system_id)
if request.method == 'POST':
form = System_Form(request.POST, instance=system)
if form.is_valid():
form.save()
return HttpResponse('Success')
else:
form = System_Form(instance=system)
context = { 'system':system,
'form':form, }
return render_to_response('some_template.html', context,
context_instance=RequestContext(request))
some_template.html
<p>make changes for {{ system }} with ID {{ system.systemID }}</p>
<form method='post'>
{{ form.as_p }}
<input type='submit' value='Submit'>
</form>
I'm implementing simple "grade book" application where the teacher would be able to update the grades w/o being allowed to change the students' names (at least not on the update grade page). To do this I'm using one of the read-only tricks, the simplest one. The problem is that after the SUBMIT the view is re-displayed with 'blank' values for the students. I'd like the students' names to re-appear.
Below is the simplest example that exhibits this problem. (This is poor DB design, I know, I've extracted just the relevant parts of the code to showcase the problem. In the real example, student is in its own table but the problem still exists there.)
models.py
class Grade1(models.Model):
student = models.CharField(max_length=50, unique=True)
finalGrade = models.CharField(max_length=3)
class Grade1OForm(ModelForm):
student = forms.CharField(max_length=50, required=False)
def __init__(self, *args, **kwargs):
super(Grade1OForm,self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.id:
self.fields['student'].widget.attrs['readonly'] = True
self.fields['student'].widget.attrs['disabled'] = 'disabled'
def clean_student(self):
instance = getattr(self,'instance',None)
if instance:
return instance.student
else:
return self.cleaned_data.get('student',None)
class Meta:
model=Grade1
views.py
from django.forms.models import modelformset_factory
def modifyAllGrades1(request):
gradeFormSetFactory = modelformset_factory(Grade1, form=Grade1OForm, extra=0)
studentQueryset = Grade1.objects.all()
if request.method=='POST':
myGradeFormSet = gradeFormSetFactory(request.POST, queryset=studentQueryset)
if myGradeFormSet.is_valid():
myGradeFormSet.save()
info = "successfully modified"
else:
myGradeFormSet = gradeFormSetFactory(queryset=studentQueryset)
return render_to_response('grades/modifyAllGrades.html',locals())
template
<p>{{ info }}</p>
<form method="POST" action="">
<table>
{{ myGradeFormSet.management_form }}
{% for myform in myGradeFormSet.forms %}
{# myform.as_table #}
<tr>
{% for field in myform %}
<td> {{ field }} {{ field.errors }} </td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit" value="Submit">
</form>
Your way of displaying the readonly field is the problem.
Since the student field is disabled, the form submit will not have that as the input, so the error form that is displayed with validation error messages don't get the initial value.
That is why ReadOnly Widget has to be more complex than just being a html disabled field.
Try using a real ReadOnlyWidget, one that overrides _has_changed.
Following is what I use. For instantiation, it takes the original_value and optionally display_value, if it is different.
class ReadOnlyWidget(forms.Widget):
def __init__(self, original_value, display_value=None):
self.original_value = original_value
if display_value:
self.display_value = display_value
super(ReadOnlyWidget, self).__init__()
def _has_changed(self, initial, data):
return False
def render(self, name, value, attrs=None):
if self.display_value is not None:
return unicode(self.display_value)
return unicode(self.original_value)
def value_from_datadict(self, data, files, name):
return self.original_value
I'm stretching myself a little here, so some thoughts:
% Have you sniffed the traffic to see exactly what's being sent between browser and server?
% Do you need to send the student name as a hidden field (your db update thing may assume you want student blank if you don't)?
% Have you looked at the source of your HTML after Python parses it?