The following shows up instead of a field in my template.
<django.contrib.localflavor.us.forms.USStateSelect object at 0x92b136c>
my template has
{{ form.state }}
what could the issue be?
class RegistrationForm(forms.Form):
first_name = forms.CharField(max_length=20)
last_name = forms.CharField(max_length=20)
phone = USPhoneNumberField()
address1 = forms.CharField(max_length=45)
address2 = forms.CharField(max_length=45)
city = forms.CharField(max_length=50)
state = USStateSelect()
zip = USZipCodeField()
also is there anyway i can make the state and zip optional?
To limit the choices to a drop down list, use us.us_states.STATE_CHOICES in your model, and use us.forms.USStateField() instead of us.forms.USStateSelect() in your forms.
To make a field optional in a form, add blank = True to that field in the model.
from django.contrib.localflavor.us.us_states import STATE_CHOICES
from django.contrib.localflavor.us.models import USStateField
class ExampleLocation(models.Model):
address1 = models.CharField(max_length=45) #this is not optional in a form
address2 = models.CharField(max_length=45, blank = True) #this is made optional
state = USStateField(choices = STATE_CHOICES)
Instead of STATE_CHOICES, there are several options you can find in the localflavor documentation. STATE_CHOICES is the most inclusive, but that may not be what you desire. If you just want 50 states, plus DC, use US_STATES.
This answer assumes you're using ModelForms. If you aren't, you should be. Once you've made your model, you should follow DRY and create basic forms like so:
from django.forms import ModelForm
class ExampleForm(ModelForm):
class Meta:
model = ExampleLocation
And it inherits your fields from your model. You can customize what fields are available, if you don't want the whole model, with other class Meta options like fields or exclude. Model forms are just as customizable as any other form, they just start with the assumption of your model's fields.
Related
I use Django Model Form:
class Fruit(models.Model):
name = models.CharField(max_length=40)
class Box(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=199)
fruit = models.ManyToManyField(Fruit)
and forms.py:
class BoxModelForm(ModelForm):
class Meta:
model = Box
I have default django ManyToMany widget in form:
http://nov.imghost.us/ly5M.png
How can I change this to input (text type) and if I type into this input:
apple,banana,lemon - comma separated
this Fruit will be created?
As stated here in the documentation :https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#overriding-the-default-fields
You can add a widgets attribute to the Meta of your Modelform to change the default widgets used in the form.
In your case it would be something like this :
class BoxModelForm(ModelForm):
class Meta:
model = Box
widgets = {
'fruit': TheWidgetYouWantToUse(),
}
But actually for the behavior you want to achieve, you could proceed another way.
You should add an extra text field, and write the addition/removal of fruits in the save step, while checking the validity of the differents tags in the clean step.
class BoxModelForm(ModelForm):
fruit_selector = forms.TextField(
max_length=255,
tag = 'Whatever'
)
class Meta:
model = Box
fields = ['user','name']
def clean_fruit_selector(self):
data = self.cleaned_data['fruit_selector']
# Check that data are corrects ie the string is correctly formatted
# If not raise validation error
....
fruit_tags = data.split(",")
#Check that all tags are fruit or raise a validation error
...
return data #or only the list of correct tags
def save(self, commit=True):
instance = super(MyForm, self).save(commit=False)
# Compare the list of tags fruit_tags with self.instance.fruit.all()
....
# Take the right actions
if commit:
instance.save()
return instance
Look into this page for more details on how to change the field validation https://docs.djangoproject.com/en/dev/ref/forms/validation/
This is just a schematic.
django-taggit is a perfect app for this use case.
Define your models like this:
from taggit.managers import TaggableManager
from taggit.models import TagBase, GenericTaggedItemBase
class Fruit(TagBase):
class Meta:
verbose_name = "Fruit"
verbose_name_plural = "Fruits"
class TaggedFruit(GenericTaggedItemBase):
tag = models.ForeignKey(Fruit,
related_name="%(app_label)s_%(class)s_items")
class Box(models.Model):
name = models.CharField(max_length=199)
fruits = TaggableManager(through=TaggedFruit)
Then create basic model form:
class BoxModelForm(ModelForm):
class Meta:
model = Box
And that's it! You can now add fruit tags into your box, separated by comma. In case the fruit doesn't exist, it will be added into Fruit table. Read the docs for more details on how to use django-taggit.
You can use it together with jquery based Selectize.js.
I'm trying to make a form to edit the value of a ManyToMany field from its parent model. As an example, I have something similar to these three models:
class Language(models.Model):
label = models.CharField()
class Word(models.Model):
language = models.ForeignKey(Language)
word = models.CharField()
entries = models.ManyToManyField(Entries, null=True, blank=True)
class Entries(models.Model):
entry = models.CharField()
Each Language will have about 50 words. Each Word will have one or two entries each.
I'm generating the formset to edit the entries for a given language like this:
class WordForm(forms.ModelForm):
class Meta:
model = Word
hidden = ('language', )
PronounFormSet = inlineformset_factory(Language, Word,
can_delete=False, extra=0, form=WordForm)
This gives me a <select> which allows me to add/remove an Entry into Word.entries. However, I want to allow the user to edit the field Entries.entry directly (i.e. the field Entries.entry should be in a CharField(). How do I modify my WordForm to allow this?
I know there are probably better ways to do this (e.g. a different database schema), but I'm heavily constrained by a legacy implementation.
If you want the entry to be a text field, you can add a custom field to your ModelForm, and make sure the default field is not shown by explicitly identifying which fields should be shown:
class WordForm(forms.ModelForm):
class Meta:
model = Word
hidden = ('language', )
fields = ('word',)
entries = forms.CharField()
Your form validation logic should be like this:
for form in formset:
obj = form.save(commit=False)
obj.language = # select the language here
obj.save()
entry = Entries.objects.get_or_create(entry=form.cleaned_fields['entries'])
obj.entries.add(entry)
obj.save()
Keep in mind with this implementation, you can't edit fields using this form (since the character field will always be empty when the form is rendered).
I'm trying to access a field of a foreign key within a Tabular Inline in the Django Admin.
Despite my best efforts I can't seem to get it working. My current code is:
class RankingInline(admin.TabularInline):
model = BestBuy.products.through
fields = ('product', 'account_type', 'rank')
readonly_fields = ('product', 'rank')
ordering = ('rank',)
extra = 0
def account_type(self, obj):
return obj.products.account_type
Which results in:
'RankingInline.fields' refers to field 'account_type' that is missing from the form.
I have also tried using the model__field method, which I used as:
fields = ('product', 'product__account_type', 'rank')
Which results in:
'RankingInline.fields' refers to field 'product__account_type' that is missing from the form.
The models are defined as so:
class Product(BaseModel):
account_type = models.CharField(choices=ACCOUNT_TYPE_OPTIONS, verbose_name='Account Type', max_length=1, default='P')
class Ranking(models.Model):
product = models.ForeignKey(Product)
bestbuy = models.ForeignKey(BestBuy)
rank = models.IntegerField(null=True, blank = True)
class BestBuy(BaseModel):
products = models.ManyToManyField(Product, through='Ranking')
class BaseModel(models.Model):
title = models.CharField(max_length = TODO_LENGTH)
slug = models.CharField(max_length = TODO_LENGTH, help_text = """The slug is a url encoded version of your title and is used to create the web address""")
created_date = models.DateTimeField(auto_now_add = True)
last_updated = models.DateTimeField(auto_now = True)
What am I doing wrong?
I think what you are looking for is nested inlines since you want to expand "Product" as inline within RankingInline. At present Django does not have such feature built in. This question is relevant: Nested inlines in the Django admin?
You can also look at "Working with many-to-many intermediary models" section in Django DOC. That might be useful.
Actually Django will show you a small green '+' button besides the inline product field entry which you can use to create a new product to assign to your current entry for BestBuy. This might be an alternative for you to use.
You simply need to add the method-field to readonly_fields:
readonly_fields = ('product', 'rank', 'account_type')
Your new field account_type should be defined in ModelAdmin (i.e. RankingAdmin) not in TabularInline (i. e. RankingInline). It should be only accessed from TabularInline.
I have a Model that looks like:
class MyModel(Model):
name = models.TextField(blank=True, default='')
bio = models.TextField(blank=True, default='')
and a ModelForm that looks like:
class MyModelForm(ModelForm):
class Meta:
model = MyModel
fields = ('name', 'bio')
When I create/ init my form like this:
form = MyModelForm(instance=my_model, data={'bio': 'this is me'}) # where my_model has a name already set
then:
form.is_valid() # evaluates to true
form.save() # overwrites the name field in my_model and makes it blank!
Is this the expected behaviour? How would I change this so that I can ensure that if a field is not specified in the form, but it exists already in the instance that it is not overwritten with an empty string?
Note that Django only sets the name field to empty because you have set blank=True. If the field was required, there would be a validation error instead.
Recently there was a discussion on the django-developers group about changing this behaviour. The core developers Adrian and Paul were in favour of the current behaviour.
So, if you're going to use model forms for this view with models where you use blank=True, you'll need to include all the model fields in your data dict. You can use the function model_to_dict for this.
from django.forms.models import model_to_dict
data = model_to_dict(my_model)
data.update({'bio': 'this is me'}) # or data.update(request.POST)
form = MyModelForm(instance=my_model, data=data)
Providing the instance argument to a ModelForm is the same as passing initial, i.e. they serve the same purpose. What gets save is always the data. If a field in that dict is empty, then it will be when the model is saved as well.
If you want to maintain the entire state of the model when only dealing with a single field as data. Then, try something like the following:
data = my_model.__dict__
data['bio'] = request.POST.get('bio')
MyModelForm(instance=my_model, data=data)
If you want to pass initial data to the form, use initial instead of data
MyModelForm(instance=my_model, initial={'bio': 'this is me'})
^
[edit]
If you have included the field for name in your form
fields = ('name', 'bio')
but do not pass any data for "name"
data={'bio': 'this is me'}
The form field will behave as if the name had been submitted blank.
Which is allowed in your model, so is_valid() will be True
name = models.TextField(blank=True, default='')
I want to make a form used to filter searches without any field being required. For example given this code:
models.py:
class Message(models.Model):
happened = models.DateTimeField()
filename = models.CharField(max_length=512, blank=True, null=True)
message = models.TextField(blank=True, null=True)
dest = models.CharField(max_length=512, blank=True, null=True)
fromhost = models.ForeignKey(Hosts, related_name='to hosts', blank=True, null=True)
TYPE_CHOICES = ( (u'Info', u'Info'), (u'Error', u'Error'), (u'File', u'File'), (u'BPS', u'BPS'),)
type = models.CharField(max_length=7, choices=TYPE_CHOICES)
job = models.ForeignKey(Jobs)
views.py:
WHEN_CHOICES = ( (u'', ''), (1, u'Today'), (2, u'Two days'), (3, u'Three Days'), (7, u'Week'),(31, u'Month'),)
class MessageSearch(ModelForm): #Class that makes a form from a model that can be customized by placing info above the class Meta
message = forms.CharField(max_length=25, required=False)
job = forms.CharField(max_length=25, required=False)
happened = forms.CharField(max_length=14, widget=forms.Select(choices=WHEN_CHOICES), required=False)
class Meta:
model = Message
That's the code I have now. As you can see it makes a form based on a model. I redefined message in the form because I'm using an icontains filter so I didn't need a giant text box. I redefined the date mostly because I didn't want to have to mess around with dates (I hate working with dates! Who doesnt?) And I changed the jobs field because otherwise I was getting a drop down list of existing jobs and I really wanted to be able to search by common words. So I was able to mark all of those as not required
The problem is it's marking all my other fields as required because in the model they're not allowed to be blank.
Now in the model they can't be blank. If they're blank then the data is bad and I don't want it in the DB. However the form is only a filter form on a page to display the data. I'm never going to save from that form so I don't care if fields are blank or not. So is there an easy way to make all fields as required=false while still using the class Meta: model = Message format in the form? It's really handy that I can make a form directly from a model.
Also this is my first serious attempt at a django app so if something is absurdly wrong please be kind :)
You can create a custom ModelForm that suit your needs. This custom ModelForm will override the save method and set all fields to be non-required:
from django.forms import ModelForm
class SearchForm(ModelForm):
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
for key, field in self.fields.iteritems():
self.fields[key].required = False
So you could declare your forms by simply calling instead of the ModelForm, e.g.:
class MessageForm(SearchForm):
class Meta:
model = Message
You could also pass empty_permitted=True when you instantiate the form, e.g.,
form = MessageSearch(empty_permitted=True)
that way you can still have normal validation rules for when someone does enter data into the form.
I would give a try to the django-filter module :
http://django-filter.readthedocs.io/en/develop/
fields are not required. these are filters actually. It would look like this :
import django_filters
class MessageSearch(django_filters.FilterSet):
class Meta:
model = Message
fields = ['happened', 'filename', 'message', '...', ]
# django-filter has its own default widgets corresponding to the field
# type of the model, but you can tweak and subclass in a django way :
happened = django_filters.DateFromToRangeFilter()
mandatory, hidden filters can be defined if you want to narrow a list of model depending on something like user rights etc.
also : setup a filter on a 'reverse' relationship (the foreignkey is not in the filtered model : the model is referenced elsewhere in another table), is easy, just name the table where the foreign key of the filtered model field is :
# the 'tags' model has a fk like message = models.ForeignKey(Message...)
tags= django_filters.<some filter>(name='tags')
quick extendable and clean to setup.
please note I didn't wrote this module, I'm just very happy with it :)