How do you make a drop down menu that contains all of the options in localflavor's US_STATES?
I can see how to create a model that contains a field that uses the localflavor option US_STATES.
class State(models.Model):
states = models.CharField(max_length=2, choices=US_STATES , null=True, blank=True)
The field state is then in a manytomany relationship to a model called Person. How do you take this a put it in a html page?
In my view, I can only think of doing this.
def get_context_data(self, *args, **kwargs):
context = super(UserProfileUpdateView, self).get_context_data(*args, **kwargs)
context['states'] = State.objects.all()
But this only pulls the existing state options.
1) How do I pull all states into the view?
2) How can I render an html template to use the output of 1? I imagine it has something to do with the 'choices' option, but I haven't ever done that before.
Thanks
You probably want to take a look at Django's forms system. Since what you're doing here is rendering a form (I'm guessing that by "drop down menu" you mean an HTML <select> element with all the states as options), that would be the preferred way to do it.
The localflavor package includes form field classes for working with its data types, including states.
Related
I have a model like with a file defined like
models.ImageField(upload_to='folder_icons', null=True)
I want to be able to limit the choice of this icon to a few pre created choices.
I there as way I can show the user (staff member) the choices in the django admin perhaps in a dropdown ?
This is similar to where I want a field where you choose between a few different avatars. Is there a custom field somewhere that can do this ?
Thanks
Just as a starting point, you would need to override the ModelAdmin.get_form() method, which will allow you to change the type of input field that Django uses by default for your image field. Here's what it should look like:
from django.forms import Select
class YourModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
# 1. Get the form from the parent class:
form = super(YourModelAdmin, self).get_form(request, obj, **kwargs)
# 2. Change the widget:
form.base_fields['your_image_field'].widget = Select(choices=(
('', 'No Image'),
('path/to/image1.jpg', 'Image 1'),
('path/to/image2.jpg, 'Image 2'),
))
# 3. Return the form!
return form
You'll still have some other considerations - for instance, the path/location of the images themselves (placing them in the settings.MEDIA_ROOT would probably be easiest, or at least the first step in trying to make this work). I can also imagine that you might want a more sophisticated presentation of this field, so that it shows a thumbnail of the actual images (see #Cheche's answer where he suggests select2 - that gets a bit more complicated though, as you'll need to craft a custom widget).
All of that said, in terms of just altering the form field that the admin uses so that it offers a dropdown/select field, rather than a file upload field, this is how you would achieve that.
What you need is a widget to render your choices. You could try with select2 or any django adapt to this, like django-select2. Check this.
In my Django 1.10 project, I have a model:
class Contact(models.Model):
notes = models.TextField()
...and ModelForm:
class ContactForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs)
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control input-sm plain'
if field.required == True:
field.widget.attrs['required'] = ''
class Meta:
model = Contact
fields = ('notes',)
I have two questions regarding this:
Can I make Django render the notes field as div with contenteditable=true rather than textarea?
If yes, how do I automate the form.save() method?
The second question is a bit vague, so I would be grateful for a hint regarding the first question. I have read through the doc, but couldn't find relevant section :(
Question 1: Render a field with a specific HTML tag
In this case, <div contenteditable="true">...</div>
You can customize the widget used to render a form field. Basically, when you declare the field, you have to pass the argument widget=TheWidgetClass. Django has a lot of builtin widgets you can use. But you can also define your own. To know how, you will find many resources on the Internet. Simply search for "django create custom widget" on a search engine, or on SO. Possible answer: Django: How to build a custom form widget?
Since Django doesn't provide any official documentation on how to create custom widget, the smartest way would be to create a class inheriting Django's TextArea and using a custom html template based on original one.
Question 2: Automate form.save() method with such a custom widget
Basically, you have to make sure the value of the div tag is sent with other inputs values to the target view of your form, in POST data. In other words, you have to make sure the content of the div acts like the content of any form field.
Maybe this SO question can help you: one solution could be using JavaScript to copy the content of your div to a hidden textarea tag in the form when the user click on submit button.
Good luck ;)
First one can be done by using jquery. Since django will load the textarea with id='id_notes'. So after that you can select the element and do whatever you want to.
And in second one you can redefine the save method by writing you own save function in forms.py Here is an example for a url shortener.
Save method basically, defines what you want to execute when something is being committed to database.
I would like to create a formset, where each form has a dropdown pointing to a set of sales items.
Model:
class SalesItem(models.Model):
item_description = models.CharField(max_length=40)
company = models.ForeignKey(Company)
Here I create a form with a dropdown, hoping to pass in the company as a source for the dropdown. Hold on to this thought, as I think that is not possible in my scenario.
Form:
class SalesItemFSForm(Form):
sales_item = forms.ModelChoiceField(required=False, queryset = '')
def __init__(self, company, *args, **kwargs):
super(SalesItemFSForm, self).__init__(*args, **kwargs)
self.fields.sales_item.queryset = company.salesitem_set.all()
Now within my view I would like to create a formset with this form:
formset_type = formset_factory(SalesItemFSForm, extra=0)
The problem becomes right away clear, as there seem to be no way that I could pass in the company to determine the source for the dropdown.
How am I supposed to do this?
Many Thanks,
Update:
it seems Jingo cracked it. :)
A ModelForm works better than a Form. On top of it I had to add fields = {} to SalesItemFSForm, to make sure that the SalesItem's fields are not showing up in the template. Because all we are interested in is our dropdown (SalesItem).
So far so good. But now I see as many dropdowns shown as I have Salesitems. It shouldn;t show any unless the user presses a jquery button.
And I think this is the problem, we should NOT pass in
formset_type = modelformset_factory(SalesItem, form=SalesItemFSForm, extra=0)
Because our form doesn't need any instance of the SalesItem. We need a dummy Model.
That was the reason I tried to solve it initially with classic Formset instead of ModelFormset. So its kind of half way there. :)
Update 2:
Jingo, good point. Effectively I was thinking of a custom save, where I just see how many formsets are added by the user via jQuery and save it myself within the view. Literally SalesItem is a ManyToMany field. But the standard M2m widget is horrible. Hence I wanted to replace it with formsets, where each salesItem is a dropdown. The user can then add as many dropdowns (forms in formset) to the page and submit them. Then I would add the relationship in the view.
class DealType(models.Model):
deal_name = models.CharField(_(u"Deal Name"), max_length=40)
sales_item = models.ManyToManyField(SalesItem)
price = models.DecimalField(decimal_places=2, max_digits=12)
Hope this makes it clear. Maybe there is an easier way to do this. :)
Btw I also found this excellent jquery snippet code how to add/remove forms to/from a formset.
Update 3:
Indeed when instantiating the object like this, we would only get one form in the formset and can add more via jquery. Perfect!! Unless there is an easier way to achieve this. :)
salesitem_formsets = formset_type(queryset=SalesItem.objects.filter(pk=1))
However this comes back hunting you in the request.POST, since you can't just do:
salesitem_formsets = formset_type(request.POST)
It still requires the queryset to be set. Tricky situation...
I hope I understood the goal you want to achieve right. Then maybe you could use ModelForm and its available instance like this:
class SalesItemFSForm(forms.ModelForm):
class Meta:
model = SalesItem
def __init__(self, *args, **kwargs):
super(SalesItemFSForm, self).__init__(*args, **kwargs)
self.sale_items = self.instance.company.salesitem_set.all()
self.fields['sales_item'] = forms.ModelChoiceField(queryset=self.sale_items)
This is untested though and just a thought. I hope this leads into the right direction, but if its totally wrong, let me know and i will remove my answer, so that others wont be confused :).
I'd like to create a form that when viewed, the user's favorite fruits are queried from the database and displayed as follows:
<select size="4">
<option selected>Apples</option>
<option>Bananas</option>
<option>Oranges</option>
<option>Watermelon</option>
</select>
The view that uses the form will:
Get the user object.
Query the database for the user's favorite fruits. (Each is a separate object of the Fruit model.)
Load the form with the fruit choices collected in (2).
I was considering using the ChoiceField, but it looks like you cannot load the list of choices into the form dynamically, at least in a straightforward manner. Am I better off skipping the form and generating the code directly at the template? Or is there a way to load the form's ChoiceField with the user items at the view?
Also, are there any general rules of thumb that dictate where it's easier to build a form using the django form fields vs generating the form code at the template?
I found the answer in this stack overflow topic. The trick is to override the form __init__() so that it accepts a new keyword argument, which in this case is the user.
views.py snippet
context = RequestContext(request)
user = User.objects.get(username=context['user'])
form = forms.FruitForm(user=user)
forms.py snippet
from django import forms
class FruitForm(forms.Form):
fruits = forms.ModelChoiceField(queryset=Fruit.objects.all())
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(FruitForm, self).__init__(*args, **kwargs)
if user:
self.fields['fruits'].queryset = Fruit.objects.filter(user=user)
It's not that difficult. You can accomplish this easily using a modelform.
See: https://docs.djangoproject.com/en/dev/topics/forms/modelforms/
One of the strengths of the Django framework is it's form handling and validation methods. So if possible, it always better for you to use Django forms or model forms.
Create a Form or a ModelForm that will be used in you view. The differnce between the two classes is the the ModelForm is built to closely resemble a database model defined in your models.py file where a Form can have custom attributes.
from django.forms import ModelForm
class FruitForm(ModelForm):
class Meta:
model = User
fields = ('favorite-fruits', )
Given a model with ForeignKeyField (FKF) or ManyToManyField (MTMF) fields with a foreignkey to 'self' how can I prevent self (recursive) selection within the Django Admin (admin).
In short, it should be possible to prevent self (recursive) selection of a model instance in the admin. This applies when editing existing instances of a model, not creating new instances.
For example, take the following model for an article in a news app;
class Article(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField()
related_articles = models.ManyToManyField('self')
If there are 3 Article instances (title: a1-3), when editing an existing Article instance via the admin the related_articles field is represented by default by a html (multiple)select box which provides a list of ALL articles (Article.objects.all()). The user should only see and be able to select Article instances other than itself, e.g. When editing Article a1, related_articles available to select = a2, a3.
I can currently see 3 potential to ways to do this, in order of decreasing preference;
Provide a way to set the queryset providing available choices in the admin form field for the related_articles (via an exclude query filter, e.g. Article.objects.filter(~Q(id__iexact=self.id)) to exclude the current instance being edited from the list of related_articles a user can see and select from. Creation/setting of the queryset to use could occur within the constructor (__init__) of a custom Article ModelForm, or, via some kind of dynamic limit_choices_to Model option. This would require a way to grab the instance being edited to use for filtering.
Override the save_model function of the Article Model or ModelAdmin class to check for and remove itself from the related_articles before saving the instance. This still means that admin users can see and select all articles including the instance being edited (for existing articles).
Filter out self references when required for use outside the admin, e.g. templates.
The ideal solution (1) is currently possible to do via custom model forms outside of the admin as it's possible to pass in a filtered queryset variable for the instance being edited to the model form constructor. Question is, can you get at the Article instance, i.e. 'self' being edited the admin before the form is created to do the same thing.
It could be I am going about this the wrong way, but if your allowed to define a FKF / MTMF to the same model then there should be a way to have the admin - do the right thing - and prevent a user from selecting itself by excluding it in the list of available choices.
Note: Solution 2 and 3 are possible to do now and are provided to try and avoid getting these as answers, ideally i'd like to get an answer to solution 1.
Carl is correct, here's a cut and paste code sample that would go in admin.py
I find navigating the Django relationships can be tricky if you don't have a solid grasp, and a living example can be worth 1000 time more than a "go read this" (not that you don't need to understand what is happening).
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['myManyToManyField'].queryset = MyModel.objects.exclude(
id__exact=self.instance.id)
You can use a custom ModelForm in the admin (by setting the "form" attribute of your ModelAdmin subclass). So you do it the same way in the admin as you would anywhere else.
You can also override the get_form method of the ModelAdmin like so:
def get_form(self, request, obj=None, **kwargs):
"""
Modify the fields in the form that are self-referential by
removing self instance from queryset
"""
form = super().get_form(request, obj=None, **kwargs)
# obj won't exist yet for create page
if obj:
# Finds fieldnames of related fields whose model is self
rmself_fields = [f.name for f in self.model._meta.get_fields() if (
f.concrete and f.is_relation and f.related_model is self.model)]
for fieldname in rmself_fields:
form.base_fields[fieldname]._queryset =
form.base_fields[fieldname]._queryset.exclude(id=obj.id)
return form
Note that this is a on-size-fits-all solution that automatically finds self-referencing model fields and removes self from all of them :-)
I like the solution of checking at save() time:
def save(self, *args, **kwargs):
# call full_clean() that in turn will call clean()
self.full_clean()
return super().save(*args, **kwargs)
def clean(self):
obj = self
parents = set()
while obj is not None:
if obj in parents:
raise ValidationError('Loop error', code='infinite_loop')
parents.add(obj)
obj = obj.parent