I try to make the form with objectl linked to only current base object (exclude others from their model):
forms.py
class RoomTypeForm(ModelForm):
class Meta:
model = RoomType
fields = {'Name', 'Rooms', 'Beds', 'Facilities', 'Capacity'}
exclude = ('Building',)
def __init__(self, *args, **kwargs):
self.building_id = kwargs.pop('building_id')
self.fields['Capacity'].queryset = Capacity.objects.filter(
Building=self.building_id
)
views.py
def building_details(request, hotel_id, building_id):
capacity_form = CapacityForm
roomtype_form = RoomTypeForm
args = {}
args.update(csrf(request))
args['building'] = Building.objects.get(id=building_id)
args['capacity'] = Capacity.objects.filter(Building=building_id)
args['roomtypes'] = RoomType.objects.filter(Building=building_id)
args['capform'] = capacity_form
args['rtform'] = roomtype_form(building_id=building_id)
return render_to_response('building.html', args)
But I have the error:
'RoomTypeForm' object has no attribute 'fields'
in line:
self.fields['Capacity'].queryset =
Capacity.objects.filter(Building=self.building_id)
How may it be possible to make all of this will be working? Please help.
You need to call the super() class in __init__
def __init__(self, *args, **kwargs):
self.building_id = kwargs.pop('building_id')
super(RoomTypeForm, self).__init__(*args, **kwargs)
self.fields['Capacity'].queryset = Capacity.objects.filter(Building=self.building_id)
Or if you are using python3, you could just do:
super().__init__(*args, **kwargs)
Another side note, it is standard practice to specify a list or a tuple in the fields. You specified a set.
Related
I am trying to set the initial value of a field on a form. The field is not part of the model, but when I try and set it to a value the field is blank. From my research it could be because the form is "bound" which makes some sense to me, but in this case the field is not part of the model.
My form:
#Form for editing profile
class CatForm(forms.ModelForm):
pictureid = forms.CharField()
class Meta:
model = Cat
fields = ['name']
def __init__(self, *args, **kwargs):
picid = kwargs.pop("pictureid")
print(picid)
super(CatForm, self).__init__(*args, **kwargs)
self.fields['pictureid'] = forms.CharField(initial=picid, required=False)
The model:
class Cat(models.Model):
name = models.CharField(max_length=34,null=False)
From the view it is called like this:
catform = CatForm(request.POST, pictureid=instance.id)
I was expecting it to set the field to the value of the initial attribute, but it doesn't. I have tried testing it by directly adding a string, but doesn't set.
This is what seems to be working for me:
class CatForm(forms.ModelForm):
class Meta:
model = Cat
fields = ['name']
def __init__(self, *args, **kwargs):
picid = kwargs.pop("pictureid")
super(CatForm, self).__init__(*args, **kwargs)
self.fields['pictureid'] = forms.CharField(initial=picid)
I also needed to drop the "request.POST" from the call to this when initiating the form.
If you want to render the pictureid in GET request, then you can try like this:
catform = CatForm(initial={'pictureid': instance.id})
For GET request, you don't need to override the __init__ method.
But, if you want to use the Catform in POST request, to use the value of pictureid somewhere else(lets say in save method), then you will need to override __init__ method here.
class CatForm(forms.ModelForm):
pictureid = forms.CharField()
class Meta:
model = Cat
fields = ['name']
def __init__(self, *args, **kwargs):
picid = kwargs.pop("pictureid")
print(picid)
super(CatForm, self).__init__(*args, **kwargs)
self.pictureid = picid
def save(self, *args, **kwargs):
print(self.pictureid) # if you want to use it in save method
return super().save(*args, **kwargs)
I'm trying to populate a Select list of a ModelForm, with the Django groups the current users belongs to.
No errors arise, but I get only an empty Select list.
This is my code:
class ArchiveForm(forms.ModelForm):
class Meta:
model = Archive
fields = ['tags', 'version', 'sharegp']
localized_fields = None
labels = {'tags': 'Related Keywords'}
sharegp = forms.ChoiceField(label='Share with groups')
def __init__(self, user, *args, **kwargs):
#import pudb;pudb.set_trace()
self.user = user
super(ArchiveForm, self).__init__(*args, **kwargs)
self.fields['sharegp'].queryset = Group.objects.filter(user=self.user)
self.fields['sharegp'].widget.choices = self.fields['sharegp'].choices
Note that if I enable the debugger in the first line of the __init__ method, and step forward all along the function, the line:
self.fields['sharegp'].queryset
Gives the correct list containing the groups for that user, but that is not passed to the actual form.
What could I be missing? Thank you!
This is how I ended up solving this:
I was wrongly choosing the type of the field: The correct one is ModelChoiceField:
class ArchiveForm(forms.ModelForm):
class Meta:
model = Archive
fields = ['tags', 'version', 'sharegp']
localized_fields = None
labels = {'tags': 'Related Keywords'}
user = None
usergroups = None
sharegp = forms.ModelChoiceField(label='Share with groups', queryset=usergroups)
def __init__(self, user, *args, **kwargs):
self.user = user
self.usergroups = Group.objects.filter(user=self.user)
super(ArchiveForm, self).__init__(*args, **kwargs)
self.fields['sharegp'].queryset = self.usergroups
That last line is overwriting the queryset assigned in previous one. Remove it.
I have a django form class only with one widget. I would like to give a name to this widget like <select name="custom_name"></select> but Django takes the name of the variable and gives it as a name to the widget. For example:
class MultiSelectForm(forms.Form):
here_is_the_name_of_the_widget = forms.MultipleChoiceField()
So the above code will create a widget with a name:
<select name="here_is_the_name_of_the_widget">...</select>
Because i need to prototype the form i need to create this name at initialization time.
Until this moment this form works fine but with a standard pre-given name:
class MultiSelectForm(forms.Form):
multiple_select = forms.MultipleChoiceField()
def __init__(self, attrs=None, choices=(), *args, **kwargs):
self.base_fields['multiple_select'].choices = choices
self.base_fields['multiple_select'].empty_permitted = False
self.base_fields['multiple_select'].required = kwargs.get('required', False)
self.base_fields['multiple_select'].widget = MultiSelect(attrs=attrs, choices=choices)
forms.Form.__init__(self, *args, **kwargs)
So, i thought a way (a hack) to achieve what i want but isn't working as i expected. If the name i want to give to the widget is 'countries' it produces the following error:
KeyError at <project_name>
'countries'
And the code who produces that error is:
class MultiSelectForm(forms.Form):
multiple_select = forms.MultipleChoiceField()
def __init__(self, attrs=None, choices=(), *args, **kwargs):
default_name = 'multiple_select'
name = attrs.get('name', default_name)
if name != default_name:
setattr(self, name, forms.MultipleChoiceField())
del attrs['name']
'''
The error is produced in the following line...
'''
self.base_fields[str(name)].choices = choices
self.base_fields[str(name)].empty_permitted = False
self.base_fields[str(name)].required = kwargs.get('required', False)
self.base_fields[str(name)].widget = MultiSelect(attrs=attrs, choices=choices)
forms.Form.__init__(self, *args, **kwargs)
Is there a way to achieve that, or something better?
Thanks!
Here is the solution...
thanks to #Daniel Roseman
class MultiSelectForm(forms.Form):
def __init__(self, attrs=None, choices=(), *args, **kwargs):
#empty_permitted is a Form's attribute
kwargs['empty_permitted'] = False
forms.Form.__init__(self, *args, **kwargs)
name = attrs.get('name', 'multiple_select')
self.fields[name] = forms.MultipleChoiceField(
choices=choices,
required=kwargs.get('required', False),
widget=MultiSelect(attrs=attrs, choices=choices)
)
I'm not sure why you need to do this at all, but you are definitely overcomplicating things. You can just add the field dynamically to the form when you instantiate it:
class MultiSelectForm(forms.Form):
def __init__(self, *args, **kwargs):
dynamic_field = kwargs.pop('dynamic_field')
super(MultiSelectForm, self).__init__(*args, **kwargs)
self.fields[dynamic_field['name']] = forms.MultipleChoiceField(
choices=dynamic_field['choices'],
empty_permitted=False
required=dynamic_field['required'],
widget=MultiSelect(attrs=dynamic_field['attrs']))
and then pass a dictionary on instantiation:
my_form = MultiSelectForm(
dynamic_field={'name': 'countries', 'choices': (mychoices), 'required': True})
First the code:
class CommentForm(forms.ModelForm):
categories = forms.ModelChoiceField(queryset = Category.objects.all(), required = False)
class CommentAdmin(admin.ModelAdmin):
form = CommentForm
When I'm editing my comment I'd like it categories field have the initial value of what's been selected when I saved it for the last time. How do I do that?
def get_form(self, *args, **kwargs):
f = super(CommentAdmin, self).get_form(*args, **kwargs)
f.base_fields['categories'].initial = 1
return f
This code placed in CommentAdmin did the trick...
EDIT:
def __init__(self, *args, **kwargs):
super(CommentForm, self).__init__(*args, **kwargs)
self.fields['categories'].initial = self.instance.object_id
Or this code placed in CommentForm
You want to have the current model value selected by default in the generated form? If that's the case I think what you are looking for in your view is
form = CommentForm(instance = commentinstance)
Where commentinstance is the instance that you are editing.
(This would be form = CommentForm(request.POST, instance = commentinstance) in case of a POST request)
EDIT:
If you want to do this in the form, you can just provide the instance argument from __init__, like so:
def __init__(self, *args, **kwargs):
instance = kwargs.pop('instance', YOUR_DEFAULT_INSTANCE)
super(CommentForm, self).__init__(instance = instance, *args, **kwargs)
That even leaves the default instance if you do provide one from your view.
I guess there are a few ways to solve this.
Here is how I done before:
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
if 'ref' in kwargs:
ref = kwargs['ref']
item = MyModel.objects.get(pk=ref)
kwargs['instance'] = item
super(MyForm, self).__init__(*args, **kwargs)
class Meta:
model = MyModel
The important part is to put your populated model object into the keyword variable instance.
I'm having some trouble trying to understand how to create a dynamic choice field in django. I have a model set up something like:
class rider(models.Model):
user = models.ForeignKey(User)
waypoint = models.ManyToManyField(Waypoint)
class Waypoint(models.Model):
lat = models.FloatField()
lng = models.FloatField()
What I'm trying to do is create a choice Field whos values are the waypoints associated with that rider (which would be the person logged in).
Currently I'm overriding init in my forms like so:
class waypointForm(forms.Form):
def __init__(self, *args, **kwargs):
super(joinTripForm, self).__init__(*args, **kwargs)
self.fields['waypoints'] = forms.ChoiceField(choices=[ (o.id, str(o)) for o in Waypoint.objects.all()])
But all that does is list all the waypoints, they're not associated with any particular rider. Any ideas? Thanks.
you can filter the waypoints by passing the user to the form init
class waypointForm(forms.Form):
def __init__(self, user, *args, **kwargs):
super(waypointForm, self).__init__(*args, **kwargs)
self.fields['waypoints'] = forms.ChoiceField(
choices=[(o.id, str(o)) for o in Waypoint.objects.filter(user=user)]
)
from your view while initiating the form pass the user
form = waypointForm(user)
in case of model form
class waypointForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
super(waypointForm, self).__init__(*args, **kwargs)
self.fields['waypoints'] = forms.ModelChoiceField(
queryset=Waypoint.objects.filter(user=user)
)
class Meta:
model = Waypoint
There's built-in solution for your problem: ModelChoiceField.
Generally, it's always worth trying to use ModelForm when you need to create/change database objects. Works in 95% of the cases and it's much cleaner than creating your own implementation.
the problem is when you do
def __init__(self, user, *args, **kwargs):
super(waypointForm, self).__init__(*args, **kwargs)
self.fields['waypoints'] = forms.ChoiceField(choices=[ (o.id, str(o)) for o in Waypoint.objects.filter(user=user)])
in a update request, the previous value will lost!
You can declare the field as a first-class attribute of your form and just set choices dynamically in __init__:
class WaypointForm(forms.Form):
waypoints = forms.ChoiceField(choices=[])
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
waypoint_choices = [(o.id, str(o)) for o in Waypoint.objects.filter(user=user)]
self.fields['waypoints'].choices = waypoint_choices
This approach also works with a ModelChoiceField.
This approach is superior if you are using a ModelForm, and want to override choices of an autogenerated field.
How about passing the rider instance to the form while initializing it?
class WaypointForm(forms.Form):
def __init__(self, rider, *args, **kwargs):
super(joinTripForm, self).__init__(*args, **kwargs)
qs = rider.Waypoint_set.all()
self.fields['waypoints'] = forms.ChoiceField(choices=[(o.id, str(o)) for o in qs])
# In view:
rider = request.user
form = WaypointForm(rider)
If you need a dynamic choice field in django admin; This works for django >=2.1.
class CarAdminForm(forms.ModelForm):
class Meta:
model = Car
def __init__(self, *args, **kwargs):
super(CarForm, self).__init__(*args, **kwargs)
# Now you can make it dynamic.
choices = (
('audi', 'Audi'),
('tesla', 'Tesla')
)
self.fields.get('car_field').choices = choices
car_field = forms.ChoiceField(choices=[])
#admin.register(Car)
class CarAdmin(admin.ModelAdmin):
form = CarAdminForm
Hope this helps.
Underneath working solution with normal choice field.
my problem was that each user have their own CUSTOM choicefield options based on few conditions.
class SupportForm(BaseForm):
affiliated = ChoiceField(required=False, label='Fieldname', choices=[], widget=Select(attrs={'onchange': 'sysAdminCheck();'}))
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
grid_id = get_user_from_request(self.request)
for l in get_all_choices().filter(user=user_id):
admin = 'y' if l in self.core else 'n'
choice = (('%s_%s' % (l.name, admin)), ('%s' % l.name))
self.affiliated_choices.append(choice)
super(SupportForm, self).__init__(*args, **kwargs)
self.fields['affiliated'].choices = self.affiliated_choice
As pointed by Breedly and Liang, Ashok's solution will prevent you from getting the select value when posting the form.
One slightly different, but still imperfect, way to solve that would be:
class waypointForm(forms.Form):
def __init__(self, user, *args, **kwargs):
self.base_fields['waypoints'].choices = self._do_the_choicy_thing()
super(waypointForm, self).__init__(*args, **kwargs)
This could cause some concurrence problems, though.