i've built my own dynamic form, kind of following james bennetts example.
using the following lines in forms.py
def make_question_form(request):
fields = { 'venue' : forms.CharField(widget=forms.HiddenInput()),
'survey' : forms.CharField(widget=forms.HiddenInput())}
return type('Question_Form',(forms.BaseForm,), { 'base_fields': fields })
and the following in the view to build it (I know its not truely dynamic, i plan to add the dynamics next.
question_form = make_question_form(request)
question_form.base_fields['venue'] = this_venue.name
question_form.base_fields['survey'] = this_survey.name
return render_to_response("survey/questions.html", locals(), context_instance=RequestContext(request))
but im not sure what to dowith it in the template and this is the bit that isn't really covered in the tutorials.
i've worked out that the following works
{% for base_field in question_form.base_fields %}
{{ base_field.type }}
{% endfor %}
but i thought the point of building it as a form was to be able to do something like
question_form.as_p
and wrap it in my own form tags.
have i missed the point or should as_p work (it doesn't).
You haven't instantiated the form in your view. make_question_form returns a new form class - normally when you use a form class in your view you do form = MyFormClass() or form = MyFormClass(request.POST).
So you need to do form = question_form() before the render_to_response, then you'll be able to do {{ form.as_p }} in the template.
Related
I'm extending the edit template for a ModelView so that I can show some other information from the database that is relevant for determining how to edit the record in this view. I know how to extend the template and get it to work, but I can't figure out how to query an object and use it in the template.
Also I need to use the value from the model/record in querying the new object I need to pass.
Here is my code from init.py:
class MilestoneView(ModelView):
edit_template = '/admin/milestone_model/milestone_edit.html'
can_delete = True
#i need something like this to work:
referrals = Referral.query.filter_by(email=model.email)
#then i need to pass referrals into the template
admin = Admin(app, name="My App", template_mode='bootstrap3')
admin.add_view(MilestoneView(Milestone, db.session, name='Milestones'))
Then from milestone_edit.html, I want something like this to work:
{% extends 'admin/model/edit.html' %}
{% block body %}
{{ super() }}
{% for r in referrals %}
<p>{{ r.name }}</p>
{% endif %}
{% endblock %}
But of course the referrals object is not available to use in the template. How do I customize this ModelView in order to pass this object in from the init file? I've reviewed the available posts on this subject(ish) on here and haven't found an answer. Thanks in advance.
Override your view's render method, see code on Github, and test if the view being rendered is the edit view. Now you can inject any data into the kwargs parameter. For example:
class MilestoneView(ModelView):
def render(self, template, **kwargs):
# we are only interested in the edit page
if template == 'admin/model/milestone_edit.html':
# Get the model, this is just the first few lines of edit_view method
return_url = get_redirect_target() or self.get_url('.index_view')
if not self.can_edit:
return redirect(return_url)
id = get_mdict_item_or_list(request.args, 'id')
if id is None:
return redirect(return_url)
model = self.get_one(id)
if model is None:
flash(gettext('Record does not exist.'), 'error')
return redirect(return_url)
referrals = Referral.query.filter_by(email=model.email)
kwargs['referrals'] = referrals
return super(MilestoneView, self).render(template, **kwargs)
Note how the model is retrieved. This is a direct copy of the code in method edit_view code. Adjust the code for your use-case.
Use the variable referrals in your edit Jinja2 template.
The render method is called in the following routes for each view:
'/' - i.e. the list view code
'/new/' - code
'/edit/' - code
'/details/' - code
I'm using inline formsets in Django, and for each item showing one "extra" form, for adding another object.
The forms for existing objects have "Delete" checkboxes, for removing that object, which makes sense.
But also the "extra" forms have these "Delete" checkboxes... which makes no sense because there's nothing there to delete. Inline forms in the Django admin don't show these "Delete" checkboxes for "extra" forms.
How can I remove these checkboxes on the "extra" inline forms?
The inline formsets part of my template is something like this (simplified, full version on GitHub):
{% for bookimage_form in form.forms %}
{% for hidden_field in bookimage_form.hidden_fields %}
{{ hidden_field.errors }}
{% endfor %}
{{ bookimage_form.as_table }}
{% endfor %}
And here's the "Delete" checkbox that seems superfluous:
You can use the can_delete setting of the InlineModelAdmin class (TabularInline inherits from InlineModelAdmin):
class BookImageInline(admin.TabularInline):
model = BookImage
extra = 1
can_delete = False
Update for Django 3.2+ (link), you can now pass in can_delete_extra as False to formset_factory or it extended classes to remove checkbox from extra forms
can_delete_extra New in Django 3.2.
BaseFormSet.can_delete_extra
Default: True
While setting can_delete=True, specifying can_delete_extra=False will
remove the option to delete extra forms.
For anyone having Django version under 3.2 and do not wish to upgrade, please use the following method of overriding the BaseFormSet:
class CustomFormSetBase(BaseModelFormSet):
def add_fields(self, form, index):
super().add_fields(form, index)
if 'DELETE' in form.fields and form.instance.pk: # check if have instance
form.fields['DELETE'] = forms.BooleanField(
label=_('Delete'),
widget=forms.CheckboxInput(
attrs={
'class': 'form-check-input'
}
),
required=False
)
else:
form.fields.pop('DELETE', None)
YourFormSet = modelformset_factory(
formset=CustomFormSetBase,
can_delete=True,
extra=2
)
only took them 13 years to add this >.> https://code.djangoproject.com/ticket/9061
I found a way to remove the delete checkbox in the official django documentation here
you just have to add 'can_delete=false' as an argument to inlineformset_factory in your views.py file
inlineformset_factory(can_delete=false)
here's a way to get there in the template when you're looping through forms:
{% if bookimage_form.instance.pk %}
<small><b>{{ bookimage_form.DELETE.label_tag }}</b></small><br>
{{ bookimage_form.DELETE}}
{% else %}
{% endif %}
you won't be able to use the as_table() method I don't think, though. You'll have to express every other field in the form.
Here's another thing you could try out after you initialise the form, but before it goes into the context:
for f in form.forms:
if not f.instance.pk:
f.fields['DELETE'] = None
Not sure how that'll come out in the table, but you may be able to monkey with the idea.
My suggest is to render the template in nested for loops and add this:
{% if forloop.parentloop.last and forloop.last%}
not render form filds
{% else %}
render field
{% endif %}
I have a class called Features in my models.py. In my html, I am displaying a list on the right that excludes two of these Features, one is the active feature that has been selected, the other is the most recently added since they are the main content of my page. The remaining Features in the list are displayed by date and do show what I am expecting.
Now, I want to single out the first, second and third Features (title only) in THAT list so I can place them in their own separate divs - because each has unique css styling. There are probably numerous ways of doing this, but I can't seem to figure any of them out.
This is a link to my project to give a better idea of what I want (basically trying to get the content in those colored boxes on the right.)
I'm just learning Django (and Python really), so thanks for your patience and help!
HTML
{% for f in past_features %}
{% if f.title != selected_feature.title %}
{% if f.title != latest_feature.title %}
<h1>{{ f.title }}</h1>
{% endif %}
{% endif %}
{% endfor %}
VIEWS
def feature_detail(request, pk):
selected_feature = get_object_or_404(Feature, pk=pk)
latest_feature = Feature.objects.order_by('-id')[0]
past_features = Feature.objects.order_by('-pub_date')
test = Feature.objects.last()
context = {'selected_feature': selected_feature,
'latest_feature': latest_feature,
'past_features': past_features,
'test': test}
return render(request, 'gp/feature_detail.html', context)
MODELS
class Feature(models.Model):
title = models.CharField(db_index=True, max_length=100, default='')
content = models.TextField(default='')
pub_date = models.DateTimeField(db_index=True, default=datetime.now, blank=True)
def __str__(self):
return self.title
def __iter__(self):
return [
self.id,
self.title ]
You can either store the first three Features in separate variables in your context or add checks to your template loop like {% if forloop.first %} or {% if forloop.counter == 2 %}.
If all you want is to not have the
selected_feature
latest_feature
these two records out of the past_features queryset, then you can use exclude on the past_features query and pass the id's of the selected_features and latest_feature objects.
The views.py would look like:
def feature_detail(request, pk):
selected_feature = get_object_or_404(Feature, pk=pk)
latest_feature = Feature.objects.order_by('-id')[0]
# Collect all the id's present in the latest_feature
excluded_ids = [record.pk for record in latest_feature]
excluded_ids.append(selected_feature.pk)
#This would only return the objects excluding the id present in the list
past_features = Feature.objects.order_by('-pub_date').exclude(id__in=excluded_ids)
test = Feature.objects.last()
context = {'selected_feature': selected_feature,
'latest_feature': latest_feature,
'past_features': past_features,
'test': test}
return render(request, 'gp/feature_detail.html', context)
Django provides a rich ORM and well documented, go through the Queryset options for further information.
For access to a specific object in Django templates see following example:
For access to first object you can use {{ students.0 }}
For access to second object you can use {{ students.1 }}
For access to a specific field for example firstname in object 4 you can use {{ students.3.firstname }}
For access to image field in second object you can use {{ students.1.photo.url }}
For access to id in first object you can use {{ students.0.id }}
Im working on Django, i need pass two or more objects to a view, for render it in the template. I mean,
i have one object, and this object can has two or more objects from other model, in the view i have:
def infoUsuario(request,id_usuario):
user = info_productor_cultivador.objects.get(id=id_usuario)
familiar = grupo_familiar.objects.filter(familiar_de_id=user)
ctx = {'usuario':user,'familiar':familiar}
return render_to_response('usuarios.html',ctx,context_instance=RequestContext(request))
in the template:
{% for familiares in familiar %}
<p>{{familiar.primer_nombre}}</p>
{% endfor %}
The models:
class grupo_familiar(models.Model):
familiar_de = models.ForeignKey(info_productor_cultivador)
primer_nombre = models.CharField(max_length=50)
class info_productor_cultivador(models.Model):
primer_nombre = models.CharField(max_length=50)
First, instead "filter" in familiar object i has "get" but said me: "get() returned more than one grupo_familiar -- it returned 2!" looking for a solution i found it that i have to pass the "filter" query, this time i dont have errors from Django, but the "familiar" object does not render it in the template.
In other words, i think that i need is how to pass a foreign key in the view and render it in the template.
Thanks
views
from django.shortcuts import render
def info_usuario(request, id_usuario):
user = info_productor_cultivador.objects.get(id=id_usuario)
familiar = grupo_familiar.objects.filter(familiar_de_id=user)
ctx = { 'usuario': user, 'familiar': familiar }
return render(request, 'usuarios.html', ctx }
template
{% for familiares in familiar %}
<p>{{ familiar.primer_nombre }}</p>
{% endfor %}
So I have a ManageUserForm in forms.py-- it renders correctly but it doesn't pull the right data from the user i'm trying to edit.
In the template, I have a for loop that works correctly
{% for tenants in tenants %}
{{ tenants.user }} {{ tenants.type }}
{% endfor %}
This template renders the list of objects in the UserProfile. And it does it correctly. The challenge I face is updating the "tenants.type" attribute. Again, the type shows up correctly but I don't know how to update it from this template page.
#views.py
def manage_users(request):
tenants = UserProfile.objects.all()
form = ManageUserForm(request.POST or None)
if form.is_valid():
update = form.save(commit=False)
update.save()
return render_to_response('manage_users.html', locals(), context_instance=RequestContext(request))
#forms.py
class ManageUserForm(forms.ModelForm):
class Meta:
model = UserProfile
exclude = ('full_name', 'user',)
`I think I need to call an instance but I have no idea how to do so for the non-request users AND still follow the pattern for the template. The template basically is a list of users where the request user (staff user) will be able to change the data in the list.
Thank you for your help!
You have one form for one user. You need a FormSet if you want to use that form to edit multiple tenants. Editing objects and displaying them are entirely different beasts; dont' confuse them.
formset = modelformset_factory(form=ManageUserForm, queryset=tenants)
Update:
You should have one {{ form.management_form }} and the rest of the {% for form in formset %}{{ form }}{% endfor %} in one <form> tag. All of your forms are the first form in the formset.
You should rewrite your template loop to iterate through formset forms instead of tenant objects. The tenant object can be accessed through {{ form.instance }}
Update 2:
You have an extra form because you probably haven't passed in the extra=0 parameter to the modelformset_factory function. These forms are typically used to add/edit data; thus it has support for adding N blank forms for creating.