Related
I need to somehow pass the user information to ForeignKeyWidget class from resource class, where I create ForeignKey object:
class CompanyWidget(ForeignKeyWidget):
def clean(self, value, row=None, *args, **kwargs):
print(self.user, file=sys.stderr)
if not value:
return None
else:
obj, _ = Company.objects.get_or_create(
name=value,
created_by='I NEED USER INFORMATION HERE SOMEHOW',
)
return obj
What is the best way to do this?
I've tried to solve this on my own and got pretty close, but could not fit the last piece of the puzzle. You override __init__ class in resource and get user information there. Then, I could not figure out how to pass this self.user information into the class variable company.
Here is the code:
class ContactResource(resources.ModelResource):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(ContactResource, self).__init__(*args, **kwargs)
company = fields.Field(
column_name='company',
attribute='company',
widget=CompanyWidget(model=Company, field='name', user='I NEED TO PASS USER HERE FROM __INIT__'))
def after_import_instance(self, instance, new, **kwargs):
instance.created_by = kwargs['user']
If I somehow manage to pass user information in **kwargs of company variable, then I can use it downstream by overriding ForeignKeyWidget's __init__ class:
class CompanyWidget(ForeignKeyWidget):
def __init__(self, model, field='pk', *args, **kwargs):
self.model = model
self.field = field
self.user = kwargs.pop('user', None)
super(CompanyWidget, self).__init__(model, *args, **kwargs)
def clean(self, value, row=None, *args, **kwargs):
print(self.user, file=sys.stderr)
if not value:
return None
else:
obj, _ = Company.objects.get_or_create(
name=value,
created_by=self.user,
)
return obj
Any help would be appreciated, it took me forever to get here and I feel I am really close. Thank you in advance.
It turns out it is easier to implement this without using ForeignKeyWidget at all.
If you have multiple foreign keys that are not unique but have the same name (in my case, same company name created by different users), this is how you could solve the problem:
class ContactResource(resources.ModelResource):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(ContactResource, self).__init__(*args, **kwargs)
company = fields.Field(column_name='company')
class Meta:
model = Contact
skip_unchanged = True
report_skipped = True
exclude =('is_active', 'created_by')
export_order = ('id','first_name','last_name','email','phone','address','description','company','created_on','website','job_title','birthday')
def after_import_instance(self, instance, new, **kwargs):
instance.created_by = self.user # kwargs['user']
def import_field(self, field, obj, data):
field_name = self.get_field_name(field)
method = getattr(self, 'clean_%s' % field_name, None)
if method is not None:
obj = method(field, obj, data)
super(ContactResource, self).import_field(field, obj, data)
def clean_company(self, field, obj, data):
name = data[field.column_name]
company, created = Company.objects.get_or_create(name=name, created_by=self.user)
obj.company = company
return obj
I'm trying do this:
class NoClearableFileInput(ClearableFileInput):
initial_text = ''
input_text = ''
class ImageUploadForm(forms.ModelForm):
title = forms.CharField(label="TITLE", required=False,widget=forms.TextInput(attrs={'placeholder': 'name'}), label_suffix="")
image = forms.ImageField(label='NEW FILE',widget=NoClearableFileInput, label_suffix="")
class Meta:
model = Image
fields = ('title','image')
There in class NoClearableFileInput cleaned value initial_text.
In fields 'title' and 'image' use label_suffix, but from initial_text symbol ":" remained.
result
How get rid of the colons?
This just worked for me with Django 2.2:
class ImageUploadForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.label_suffix = "" # Removes : as label suffix
# ...the rest of the form code...
You have to override the label_suffix on initialization. Try making the following changes:
class ImageUploadForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
kwargs.setdefault('label_suffix', '')
super(ImageUploadForm, self).__init__(*args, **kwargs)
# ... (the rest of your code) ...
There is label_suffix=":" parameter to form's __init__ method - https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.label_suffix
So, to remove trailing colon from label, you can pass label_suffix="" when initializing form in the view, for example:
class SomeView(FormView):
...
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["label_suffix"] = ""
return kwargs
Or, you can override this parameter in form's __init__ method:
class SomeForm(forms.Form):
def __init__(self, *args, **kwargs):
kwargs["label_suffix"] = ""
super().__init__(*args, **kwargs)
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.