Is there an easy way to select the first option of a dropdown in a Django form, in cases where there is only one option available?
With easy I mean a solution that doesn't require defining an own widget, which would be quite repetitive to change in all our (model) forms.
Update because it seems like the initial question was unclear:
I want the initital option only to be selected if there is one option available. And the the way to do that is non-obvious to me, if the options are foreign key references to another model:
class Category(models.Model):
name = CharField(...)
class Post(models.Model):
category = ForeignKey(Category)
class PostForm(forms.ModelForm):
class Meta:
fields = '__all__'
[...]
Now I want the category field in the PostForm to be autoselected to the first category, if only one instance is present in the database and be -------- if there a two or more categories
What about using this :
Your_Dropdown = forms.TypedChoiceField(choices=your choises, initial='FIRST_OPTION')
As shown in documentation: Here
I don't think any explanation is required, have a look at following code
post_type_choices = (
('article', 'article'),
('tip', 'tip'),
('snippet', 'snippet'),
)
class Post(models.Model):
post_type = models.CharField(
max_length=10, choices=post_type_choices,default='article')
I did it in the get_form() like this:
def get_form(self, form_class=form_class):
form = super(WPWeekSubjectCreateView, self).get_form(form_class)
....
subjects = Subject.objects.filter(
schoolyear=schoolyear,
pk__in=subject_bearings,
lesson__teachers=teacher
).order_by('abbreviation').distinct()
form.fields['subject'].queryset = subjects
if subjects.count() == 1:
form.fields['subject'].initial = subjects.first()
return form
Related
I'm creating two many-to-many fields based on same model in a single model. I would like to show only those instances in second many-to-many field which are selected in first many to many field to further apply selection. Through which approach should i handle this behaviour.
class Params(models.Model):
name = models.CharField(max_length = 200)
comments = Model.TextField()
def __str__(self):
return self.name
class Selection(models.Model):
select_params = models.ManyToManyField(Params, blank=True, related_name = "selection_1")
edit_selected_params = models.ManyToManyField(Params, blank=True, related_name =
"selection_from_selec_params")
Thanks and regards
You can use SubQuery is think:
selected = Selection.objects.filter(
edit_selected_params__in=SubQuery(
Selection.objects.filter(select_params__isnull=False).values_list("select_params", flat=True)
)
)
You can see SubQuery docs
I am in the process of designing a couple of new models for my django app and this is what I need:
class Bookmaker(models.Model):
name = models.CharField(max_length=50)
accepted_countries = ?
restricted_countries = ?
class Country(models.Model):
name = models.CharField(max_length=50)
bookmakers = ?
So I need a model Bookmaker and a model Country and they need to be related, BUT every bookmaker should have a list of countries that are accepted and a list of countries that are excluded.
The question marks are both in the Country and in the Bookmaker models as I'm not sure which way the relation should go.
Not sure if I'm on the right path, but I'm thinking that I need a couple of many-to-many relationships.. And, which is the right way (if any) to use the same Country model (those will be different instances) in both accepted_countries and restricted_countries?
Thanks.
You should use two many to many relations with related_name to separate them:
class Bookmaker(models.Model):
name = models.CharField(max_length=50)
accepted_countries = models.ManyToManyField(Country, related_name="accepted_for")
restricted_countries = models.ManyToManyField(Country, related_name="restricted_for")
You can then use the reverse relation as:
bookmakers_for_which_county_is_accepted = Country.objects.values('accepted_for').distinct()
bookmakers_for_which_county_is_restricted = Country.objects.values('restricted_for').distinct()
docs
You need indeed some many to many:
class Bookmaker(models.Model):
name = models.CharField(max_length=50)
accepted_countries = models.ManyToMany('Country',related_name='accepted')
restricted_countries = models.ManyToMany('Country', related_name= 'restricted')
class Country(models.Model):
name = models.CharField(max_length=50)
bookmakers = models.ManyToMany(Bookmaker)
Then if you create a form to edit a bookmaker you will be abble to add the name, the accepted and restricted countries:
forms.py
class BookmakerForm(models.ModelForm):
class Meta:
model = Bookmaker
fields = ['name', 'accepted_countries', 'restricted_countries']
#for having a better default display you can change the widget:
self __init__(self, *args, **kwargs):
super(BookmakerForm, self).__init__(*args, **kwargs)
self.fields['accepted_countries'].widget = CheckboxSelectMultiple()
self.fields['restricted_countries'].widget = CheckboxSelectMultiple()
In the view by default if you just need to check if form.is_valid() and save. django modelform will manage the intermediary step for you.
The bookmakers field in Country allows you to retrieve all the bookmaker associated to a country without having to check in both accepted and restricted (it's more for convenience).
But you will need to add the bookmaker to that list on you view like:
class CreateBookmakerView(CreateView):
model = Bookmaker
form_class = BookmakerForm
success_url = reverse_lazy('your success url name here')
def form_valid(self,form):
bookmaker = form.save()
bookmaker.country_set.add(bookmaker)
bookmaker.save()
return HttpResponseRedirect(self.get_success_url())
I am building a filter for my website where people can filter by cuisine. In order to achieve this I used a model form to receive input information that sets the filter variable in a query in my view. However as you can see in the image linked below, the default select for my cuisine categories is '-------' .
How would I go about changing this to say the words 'all' and setting a value so my filter queries everything for those categories? I think it has something to do with using a form method but I have been unable to understand what is actually happening in some of the examples.
Here is my simple code
Models
class Cuisine(models.Model):
name = models.CharField()
def __str__(self):
return self.name
class Food(models.Model):
name = models.CharField()
cuisine = models.ForeignKey(Cuisine)
def __str__(self):
return self.name
Views
def home_page(request):
if request.method == 'GET':
form = FilterForm(request.GET)
if form.is_valid():
cuisine = form.cleaned_data['cuisine']
food = get_list_or_404(Food, cuisine__pk=cuisine.pk)
return render('base.html', {'food': food, 'form':form})
else:
form = FilterForm()
return render('base.html', {'form':form})
Form
class FilterForm(forms.ModelForm):
class Meta:
model = Cuisine
fields = ('name')
I wouldn't use a modelform here. You only have one field, and you're not using it to create or edit instances of Food or Cuisine. It would be simpler to use a manual form with a ModelChoiceField, to which you can pass the empty_label parameter.
class FilterForm(forms.Form):
cuisine = forms.ModelChoiceField(queryset=Cuisine.objects.all(),
empty_label="All")
(Note you could do this with the ModelForm as well, but that just makes it even more pointless, as you are now not using any of the ModelForm functionality.)
I am trying to figure out how to dynamically change a ModelForm field based on the input from a previous field.
For example, if I have these kinds of models:
class Phone(models.Model):
name = models.CharField(max_length=10)
class Series(models.Model):
name = models.CharField(max_length=10)
class Manufacturer(models.Model):
phone = models.ForeignKey('Phone')
series = models.ForeignKey('Series')
class ManufacturerForm(ModelForm):
class Meta:
model = Manufacturer
Which would generate a form (ManufacturerForm) with dropdown options for the phone and series entries in the database. Is it possible to a different set of series entries based on the phone entered by the user, for example?
I have read about using the __init__ method to accomplish this, based on what I have read on this blog post, but I am not sure how to execute this given my scenario. Or maybe there is a better way to go about achieving this that you have taken? Thanks for any advice!
EDIT: Added the form's view.
def make_ad(request):
if request.method == 'POST':
form = ManufacturerForm(request.POST, request.FILES)
if form.is_valid():
a = form.save()
a.user = request.user
a.save()
else:
form = ManufacturerForm()
variables = RequestContext(request, {
'form': form
})
return render_to_response('place.html', variables)
#super9 mentioned using ajax request to change these elements. I need to check if request.is_ajax(), but at what point should I check this in my view? And how do I add or change the queryset based on the ajax request?
Thanks for your advice.
EDIT: Trying to use django-smart-selects
Not sure how to setup my models to utilize django-smart-selects to accomplish what I am trying. Here is how I have structured my models:
from smart_selects.db_fields import ChainedForeignKey
class Phone(models.Model):
phone = models.CharField(max_length=10)
class Series(models.Model):
series = models.CharField(max_length=10)
phone = models.ForeignKey(Phone)
class SeriesModel(models.Model):
model = models.CharField(max_length=10)
series = models.ForeignKey(Series)
class Manufacturer(models.Model):
phone = models.ForeignKey(Phone)
series = ChainedForeignKey (Series, chained_field = "phone", chained_model_field = "phone")
series_model = ChainedForeignKey (SeriesModel, chained_field = "series", chained_model_field = "series")
But when I view my form (ModelForm) the fields for series_model are not chained properly to series. Am I missing something to make smart-selects work on the second layer of abstraction?
EDIT: Above code now works.
I have some models and I want to generate a multi-selection form from this data.
So the form would contain an entry for each category and the choices would be the skills in that category.
models.py
class SkillCategory(models.Model):
name = models.CharField(max_length=50)
class Skill(models.Model):
name = models.CharField(max_length=50)
category = models.ForeignKey(SkillCategory)
Is there a way to auto-generate the form fields?
I know I can manually add a 'SkillCategory' entry in the form for each SkillCategory, but the reason to have it as a model is so skills and skillcategories can be edited freely.
I want to do something like this:
(I tried this, but didn't get it to work, don't remember the exact error...)
forms.py
class SkillSelectionForm(forms.Form):
def __init__(*args, **kwargs):
super(SkillSelectionForm, self).__init__(*args, **kwargs)
for c in SkillCategory.objects.all():
category_skills = [(pk, s.name) for s in c.skill_set.all()]
setattr(self, c.name, forms.MultipleChoiceField(choices=category_skills, widget=forms.CheckboxSelectMultiple))
SOLUTION
This creates a form field entry using the SkillCategory.name and assigns choices as those in Skill. field_name/display_name are used to avoid issues with non-ascii category names.
forms.py
def get_categorized_skills():
skills = {}
for s in Skill.objects.values('pk', 'name', 'category__name').order_by('category__name'):
if s['category__name'] not in skills.keys():
skills[s['category__name']] = []
skills[s['category__name']].append((s['pk'], s['name']))
return skills
class SkillSelectionForm(forms.Form):
def __init__(self, *args, **kwargs):
super(SkillSelectionForm, self).__init__(*args, **kwargs)
skills = get_categorized_skills()
for idx, cat in enumerate(skills.keys()):
field_name = u'category-{0}'.format(idx)
display_name = cat
self.fields[field_name] = forms.MultipleChoiceField(choices=skills[cat], widget=forms.CheckboxSelectMultiple, label=display_name)
Okay so you can't set fields like that on forms.Form, for reasons which will become apparent when you see DeclarativeFieldsMetaclass, the metaclass of forms.Form (but not of forms.BaseForm). A solution which may be overkill in your case but an example of how dynamic form construction can be done, is something like this:
base_fields = [
forms.MultipleChoiceField(choices=[
(pk, s.name) for s in c.skill_set.all()
]) for c in SkillCategory.objects.all()
]
SkillSelectionForm = type('SkillSelectionForm', (forms.BaseForm,), {'base_fields': base_fields})
Take a look at creating dynamic forms in Django, from b-list.org and uswaretech.com. I've had success using these examples to dynamically create form content from models.
What you want is a Formset. This will give you a set of rows, each of which maps to a specific Skill.
See the Formset documentation and the page specifically on generating formsets for models.