Add extra data to choices in Django Admin - django

I have something like the following:
class Destination(models.Model):
name = models.CharField
picture = models.ImageField
def __unicode__(self):
return u"%s" % self.name
class Vacation(models.Model):
destination = models.ForeignKey(Destination)
When creating the model in my Django Admin interface, I'd like my Destinations to be displayed as radio buttons with the Destination name and Destination picture.
I'm using a custom add_form template so displaying radio buttons with destination name is no problem, but including the picture is difficult.
I would like to leave __unicode__(self) as-is, I only need the picture returned with the object in this admin view. Also I don't want to inline the object.
Any advice on how to do this (including how to incorporate it into the template) would be great!
EDIT: This SO post comes very close to what I need, but I would like to access the individual choice data instead of parsing it from a modified label.

This is not an admin-specific answer, but I think it should work in admin if you can use a custom template for the form.
You could make a modified widget (probably as a subclass of an existing Django widget), that send extra fields from the model to a custom widget template.
You could also render the form manually in the template where it's displayed, and make an inclusion tag that fetches any extra information using the id of your destination object, which is passed as the value of the option.
For example:
your_template.html
{% load destinations %}
{% for opt in form.destination %}
{{ opt.tag }}
{% destination opt.data.value %}
{% endfor %}
destinations.py (in your_app/templatetags)
from django import template
from your_app.models import Destination
register = template.Library()
#register.inclusion_tag('your_app/destination-option.html')
def destination(id):
destination=Destination.objects.filter(id=int(id)).first()
return {'destination':destination}
destination-option.html
<!-- any formatting you prefer -->
{{destination.title}}
<img src="{{destination.picture.image_url}}">

Related

Django form validation/saving for a template with varying quantity and order of text, images, and forms

I am trying to create a Django app where a user (say a researcher or a teacher) can create content for other users (participants or students) where any given page may include any number of content items (such as surveys questions, text, and/or images), and these items can be arranged by the teacher in any order on a page.
I'm taking an approach similar to the last chapters (10-12) of the book Django by Example. The app from those chapters comes very close to giving an example of my case using an e-Learning platform app, but they only allow teachers to create Content like Text and Images, they do not allow teachers to create survey questions for students. Here is the general approach that I am following.
The book models content for text and images for a given page like so:
class Content(models.Model):
page = models.ForeignKey(Page, related_name='contents')
content_type = models.ForeignKey(ContentType,
limit_choices_to={'model__in':('text',
'image')})
object_id = models.PositiveIntegerField()
item = GenericForeignKey('content_type', 'object_id')
order = OrderField(blank=True, for_fields=['page'])
class Meta:
ordering = ['order']
class ItemBase(models.Model):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
def render(self):
return render_to_string('courses/content/{}.html'.format(
self._meta.model_name), {'item': self})
class Text(ItemBase):
content = models.TextField()
class Image(ItemBase):
file = models.FileField(upload_to='images')
There's an interface for teachers to fill out and order this content for a page.
Content is rendered within a template like so:
<div class="page">
{% for content in page.contents.all %}
{% with item=content.item %}
<h2>{{ item.title }}</h2>
{{ item.render }}
{% endwith %}
{% endfor %}
</div>
And the html used in the render method for a Text Content is simply:
{{item.content|linebreaks|safe }}
I don't find it difficult to extend this with QuestionTypes that extend ItemBase (similar to Text and Image models), as well as creating a StudentAnswer model for storing answers in the database.
I would probably override ItemBase render and put it into the QuestionType model so that I could pass a StudentAnswer model form into the context for the QuestionType html file.
I could then just have the html that is rendered contain {{ form }} for simplicity sake, and we now have a question that acts like any other content item and can be ordered with all of the others. This assumes that the HTML that contains the content has a form tag that encompass all of the rendered content items.
My problem is handling the POST request in the view. I have no idea how to validate and save answers from multiple forms on any given single page generated in this way. Examples I find assume a consistent number of forms that you know by name and are of different types.
I've also played around with the idea of creating a single dynamically generated form, but the examples I've seen for that assume you will loop through the fields in the template and display them in order rather than my requirement that a teacher determines the order which may include text or images in-between questions.
Is there a way to create what I'm looking for or can someone point me in the right direction?

How to display multiple forms of a single model in Django templates?

I have this model Note:
class Note(models.Model):
category = models.ForeignKey(Category)
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=40)
text = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
And I want to display this form:
class NoteEditForm(forms.ModelForm):
class Meta:
model = Note
fields = ('title', 'text')
in a template, but I want it to appear for each existing Note object in the database (it has to be that way). I've done something like that but then I hardcoded the form, pointing to the edit view URL with each object pk as a parameter; but I'm sure it has to be a clearer way, just I haven't found it. Could you guys help me with that? Thanks!
The easiest way to do this is to use a formset (also see model formsets).
For your Note model and NoteEditForm you could do something like this. You'd usually put this wherever you've defined your NoteEditForm but it can go in another file, such as views.py.
from django.forms import modelformset_factory
NoteEditFormSet = modelformset_factory(Note, form=NoteEditForm)
Using NoteEditFormSet in a view and template is almost the same as using a regular form, but there are a few differences to be aware of if you want to do anything complicated so have a look at the docs (view and template). If that's not clear enough, add a few details of what you're trying to do in your view and template and I'll try to help.
By default the formset will use Note.objects.all() as its queryset, which is what you say you want, but you can change that (details are covered in the docs).
Update:
To save an individual Note with an AJAX request I would add a second view to handle those requests. So if your formset for all Notes is served by a view at /notes/, you could add a view to handle your AJAX request at /notes/<note_id>/ (obviously just an example, adjust to fit your URL structure).
Then your JS on the /notes/ page is responsible for serializing the data for a single note and making the request to /notes/<note_id>/ (remember the CSRF token).
The HTML inputs generated for the formset have their IDs prefixed with something like id_form-<number>- and there are hidden inputs containing Note primary keys which will let you work out which ID prefix applies to each note.
I would think about doing it like this
{% for note in Notequeryset %}
<form action={% url 'url_name_to_form' pk={{note.pk}} %}>
{{form.as_p}}
</form>
{% endfor %}
Let me know what you think

How to set the default option for a select field using Jinja templates

I need to add default option value to a select field using Jinja templates.
form.py
class TeamForm(Form):
user = SelectField(u'Team Member')
views.py
class myview(request,id):
form = TeamForm(request.form)
members =db.session.query(teams).filter_by(t_id = id).all()
return render_template("members.html",form=form,members=members)
member.html
<table>
{% for member in members%}
<tr>
<td>{{ form.user(class_="form-control",value=user.id) }}</td>
</tr>
{% endfor %}
</table>
The assigned option value is not shown in the output.
I have to loop the select fields depending upon the members in team.
For example, if a team has 3 members, I will display the three select fields and auto select those three members.
You have two questions in play here:
To implement default values in your form, just use the default=(value, label) option for the SelectField class in form.py.
Assume that you want to default to Johnny as your team member and the numerical value of that option is 0. Then you can do something like this using default=(value, label):
class TeamForm(Form):
user = SelectField(u'Team Member', default=(0, "Johnny"))
Just pass in a tuple in the form (value,label). Your values can be int's, strings, whatever. (You're also missing a Submit button, which I assume is accidental.)
I'm not sure what you're doing in your views. You say, "Here I have to loop the select fields depending upon the members in team. For example if team having 3 members, I will display the three select field with auto selected by those three members." Are you saying that you just need to show these to the user in a drop-down field when you render the form, or are you having trouble actually displaying query results to the user when the template is rendered?
Because you have a form in play, I assume you will be submitting data to a server via a POST. A typical view pattern creates a blank form object and passes it to the user when the template is rendered via a GET request. When the user enters data and submits it, then it is sent via a POST request. I also noticed that you used a class for your views. I suggest using a decorator instead for your routing and have it call a function.
If, say, you have three options in play in your form and you want to show them to your user in a drop-down, then you'll want to implement something like this:
form.py:
# Sample Form class; choices can be overridden later.
class TeamForm(Form):
user = SelectField(u'Team Member', default=(0, "Johnny"), choices=[(0, "Johnny"), (1, "Sarah"), (2, "Bill")])
submit= SubmitField('Submit')
view.py:
#app.route('/team', methods=['GET','POST'])
def team_view(id):
user_selection = None
form = TeamForm()
# This code block will only execute on POST
if request.method == 'POST':
if form.validate_on_submit():
user_selection = form.user.data
form.user.data = ''
return redirect(url_for(some-other-page.html))
members =db.session.query(teams).filter_by(t_id = id).all()
# This next line will override the default choices in your form.
form.user.choices = [(member.id, member.name) for member in members]
return render_template("members.html",form=form)
member.html:
Option 1:
<!-- Assume you're using Flask-Bootstrap & WTForms -->
{% import "bootstrap/wtf.html" as wtf %}
<!-- Render your Team form; this is where you call to your form object -->
{{ wtf.quick_form(form) }}
Option 2:
<!-- Manually construct your template -->
<form method="POST" action="/team">
<div>{{ form.user }}</div>
<div>{{ form.submit() }}</div>
</form>
I found a solution that worked perfectly for me.
If you look inside the form object of type SelectField, you will see that Jinja will render as selected the field that is by default.Ex:
type = SelectField('Company Type', choices=type_choices, default=1)
In the example above Jinja will render a select whose option will have a selected value where value=1.
Checking the form object instantiated in my views, I verified that there are three fields to fulfill this condition:
form.type.data = "1" # ---> string
form.type.default = 1 # ---> int
form.type.object_data = 1 # ---> int
So, replacing these three values with data brought from database, inside views, I was able to create a dynamic default select in Jinja, without having to make any changes to my HTML or forms:
form = EnterpriseForm()
result = "query to database"
form.type.data = str(result.id_enterprise_type)
form.type.default = result.id_enterprise_type
form.type.object_data = result.id_enterprise_type
I hope this information helps others.

Overiding placholders in django cms?

Hey im using the placeholder fields from django cms in some of my custom cms apps. Basically what im trying to achieve is specific styles and filters for the same placeholder fields being used in different templates.
for example if i have a model for vacancies that looks something like this:
from django.db import models
from cms.models.fields import PlaceholderField
# Create your models here.
class Vaccancy(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, unique = True)
ref_number = models.CharField(max_length=255)
info = PlaceholderField('info')
active = models.BooleanField(default=True, verbose_name="posistion active?")
and another model that also utilizes the placeholder field in a similar way. What i hoped i could do is overide the tex.html template then have some conditional logic to detect the name of the placeholder like so
{% ifequal placeholder "info" %}
{{ body|truncatewords:200|safe }}
{% endifequal %} the aim of this is so i can specify different filters like truncatewords etc as i dont want to apply this to every placeholder that uses a text plugin!
hope that was clear enough! cheers for any help!
If you use placeholder fields, you have to check for placeholder.slot, also note that {% if placeholder.slot == "info" %} seems a bit nicer than ifequal :D

Django template can't access a form field if it is inside an array

Fairly new to Django here, so I don't know if I'm just not getting it or this is a bug. Let's say I have a form class:
class SurveyTwo(forms.Form):
food = [forms.BooleanField(required=False, initial=False, label="Seafood")]
Then, in the corresponding template, I am trying to access this by typing
{{ form.food.0 }}
When I do this, I get on my page:
<django.forms.fields.BooleanField object at 0x1c5b990>
Not the "Seafood" checkbox I was looking for. I can access the label just fine by doing {{ form.food.0.label }} but the checkbox just appears as that string. Should I be able to do this or not?
Essentially what I am trying to do is to pass an array of checkboxes to my form template, rather than having to define each form variable/field. I want to do this because I'm going to have a large number of checkboxes and want to be able to lay them out in a certain order (with a 2D array), rather than define them and lay them all out manually. If I can't do the above, does anyone know of a simpler solution? Thanks.
Mark
You can register simple template tag:
from django import template
register = template.Library()
#register.simple_tag
def bound_field(form, name):
""" returns bound field """
return form.__getitem__(name)
Then in template you just use:
{% bound_field form <field_name> %}
where is name of field.
If you have dynamicly generated fields that names you don't know you can access to them via fields.keys in this case generating all fields will look like
{% for name in form.fields.keys %}
{% bound_field form name %}
{% endfor %}