Django create form field with a model of the current user - python

I have a simple app, where a user has multiple businesses, and each business has multiple products, what I´m trying to do is a make product creatView, where i can select a business from the ones owned by the current user. I tryed editing the init() method of the ModelForm like this:
class Producto_Form(forms.ModelForm):
class Meta:
model = Producto_Model
fields = ("Nombre_Producto","Negocio","Descripcion_Producto",'Precio_Producto','Tags',"Foto")
def __init__(self, *args, **kwargs):
super(Producto_Form, self).__init__(*args, **kwargs)
self.fields['Negocio'].queryset = Negocio_Model.objects.all().filter(Administrador_id=kwargs['user'].id)
and then i changed the get_form_kwargs from the create product view like this:
class crear_producto(LoginRequiredMixin, CreateView):
template_name = "tienda/crear_producto.html"
form_class= Producto_Form
success_url = reverse_lazy('tienda_app:crear_producto')
login_url = reverse_lazy('register_app:logIn')
def get_form_kwargs(self, *args, **kwargs):
kwargs = super().get_form_kwargs(*args, **kwargs)
kwargs['user'] = self.request.user
return kwargs
I was following this question but I keep getting the error __init__() got an unexpected keyword argument 'user'

So everything is almost fine but you must pass the user variable to the form init as the kwargs, also, on the queryset dont call it like kwargs['user'] and just call user, something like this:
def __init__(self, user, *args, **kwargs):
super(Producto_Form, self).__init__(*args, **kwargs)
self.fields['Negocio'].queryset = Negocio_Model.objects.all().filter(Administrador_id=user.id)
also I changed the super() constructor on the get_form_kwargslike this:
def get_form_kwargs(self, *args, **kwargs):
kwargs = super(crear_producto, self).get_form_kwargs(*args, **kwargs)
kwargs['user'] = self.request.user
return kwargs

Related

Form Problems - Setting Initial Value

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)

Django How to pre-populate a form field with a argument form context

I have the following view below. I would like to use 'email' from the get_context_data method to pre-populate a form field in my UserAuthenticationForm form, how is this possible?
View:
class CampaignLoginView(LoginView):
model = Campaign
form_class = UserAuthenticationForm
def get_context_data(self, *args, **kwargs):
context = super(CampaignLoginView, self).get_context_data(*args, **kwargs)
user = AppUser.objects.get(pk=1)
context['email'] = user.email
Form:
class UserAuthenticationForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super(UserAuthenticationForm, self).__init__(*args, **kwargs)
Or is there is a way 'I should be doing it'?
What you want to do is pass initial data to the form. Assuming that LoginView is a class-based view that includes the FormMixin, this is best done in the get_form_kwargs method:
def get_form_kwargs(self):
kwargs = super(CampaignLoginView, self).get_form_kwargs()
kwargs['initial']['email'] = self.request.user.email
return kwargs

ModelChoiceField initial value in change form

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.

How do I filter ManyToManyField choices in a Django ModelForm?

I want to filter ManyToManyField choices in my ModelForm:
class MyForm(forms.ModelForm):
class Meta:
model = Entity
fields = ['parent_entities']
def __init__(self, *args, **kwargs):
self.root_entity = kwargs.pop('root_entity')
self.Meta.fields['parent_entities'].queryset = Entity.objects.filter(root_entity=self.root_entity)
super(MyForm, self).__init__(*args, **kwargs)
I tried a lot of different code I've seen but nothing has worked yet.
I guess my problem is that I can't get this 'parent_entities' field.
With this code, I have the error :
list indices must be integers, not str
def __init__(self, *args, **kwargs):
# First pop your kwargs that may bother the parent __init__ method
self.root_entity = kwargs.pop('root_entity')
# Then, let the ModelForm initialize:
super(MyForm, self).__init__(*args, **kwargs)
# Finally, access the fields dict that was created by the super().__init__ call
self.fields['parent_entities'].queryset = Entity.objects.filter(root_entity=self.root_entity)

Accessing parent model instance from modelform of admin inline

I'm using a TabularInline in Django's admin, configured to show one extra blank form.
class MyChildInline(admin.TabularInline):
model = MyChildModel
form = MyChildInlineForm
extra = 1
The model looks like MyParentModel->MyChildModel->MyInlineForm.
I'm using a custom form so I can dynamically lookup values and populate choices in a field. e.g.
class MyChildInlineForm(ModelForm):
my_choice_field = forms.ChoiceField()
def __init__(self, *args, **kwargs):
super(MyChildInlineForm, self).__init__(*args, **kwargs)
# Lookup ID of parent model.
parent_id = None
if "parent_id" in kwargs:
parent_id = kwargs.pop("parent_id")
elif self.instance.parent_id:
parent_id = self.instance.parent_id
elif self.is_bound:
parent_id = self.data['%s-parent'% self.prefix]
if parent_id:
parent = MyParentModel.objects.get(id=parent_id)
if rev:
qs = parent.get_choices()
self.fields['my_choice_field'].choices = [(r.name,r.value) for r in qs]
This works fine for the inline records bound to an actual record, but for the extra blank form, it doesn't display any values in my choice field, since it doesn't have any record id and there can't lookup the associated MyParentModel record.
I've inspected all the values I could find (args, kwargs, self.data, self.instance, etc) but I can't find any way to access the parent object the tabular inline is bound to. Is there any way to do this?
Update: As of Django 1.9, there is a def get_form_kwargs(self, index) method in the BaseFormSet class. Hence, overriding that passes the data to the form.
This would be the Python 3 / Django 1.9+ version:
class MyFormSet(BaseInlineFormSet):
def get_form_kwargs(self, index):
kwargs = super().get_form_kwargs(index)
kwargs['parent_object'] = self.instance
return kwargs
class MyForm(forms.ModelForm):
def __init__(self, *args, parent_object, **kwargs):
self.parent_object = parent_object
super(MyForm, self).__init__(*args, **kwargs)
class MyChildInline(admin.TabularInline):
formset = MyFormSet
form = MyForm
For Django 1.8 and below:
To pass a value of a formset to the individual forms, you'd have to see how they are constructed. An editor/IDE with "jump to definition" really helps here to dive into the ModelAdmin code, and learn about the inlineformset_factory and it's BaseInlineFormSet class.
From there you'll find that the form is constructed in _construct_form() and you can override that to pass extra parameters. It will likely look something like this:
class MyFormSet(BaseInlineFormSet):
def _construct_form(self, i, **kwargs):
kwargs['parent_object'] = self.instance
return super(MyFormSet, self)._construct_form(i, **kwargs)
#property
def empty_form(self):
form = self.form(
auto_id=self.auto_id,
prefix=self.add_prefix('__prefix__'),
empty_permitted=True,
parent_object=self.instance,
)
self.add_fields(form, None)
return form
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.parent_object = kwargs.pop('parent_object', None)
super(MyForm, self).__init__(*args, **kwargs)
class MyChildInline(admin.TabularInline):
formset = MyFormSet
form = MyForm
Yes, this involves a private _construct_form function.
update Note: This doesn't cover the empty_form, hence your form code needs to accept the parameters optionally.
I'm using Django 1.10 and it works for me:
Create a FormSet and put the parent object into kwargs:
class MyFormSet(BaseInlineFormSet):
def get_form_kwargs(self, index):
kwargs = super(MyFormSet, self).get_form_kwargs(index)
kwargs.update({'parent': self.instance})
return kwargs
Create a Form and pop an atribute before super called
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
parent = kwargs.pop('parent')
super(MyForm, self).__init__(*args, **kwargs)
# do whatever you need to with parent
Put that in the inline admin:
class MyModelInline(admin.StackedInline):
model = MyModel
fields = ('my_fields', )
form = MyFrom
formset = MyFormSet
AdminModel has some methods like get_formsets. It receives an object and returns a bunch of formsets. I think you can add some info about parent object to that formset classes and use it later in formset's __init__
Expanding on ilvar's answer a bit, If the form field of interest is constructed from a model field, you can use the following construction to apply custom behavior to it:
class MyChildInline(admin.TabularInline):
model = MyChildModel
extra = 1
def get_formset(self, request, parent=None, **kwargs):
def formfield_callback(db_field):
"""
Constructor of the formfield given the model field.
"""
formfield = self.formfield_for_dbfield(db_field, request=request)
if db_field.name == 'my_choice_field' and parent is not None:
formfield.choices = parent.get_choices()
return formfield
return super(MyChildInline, self).get_formset(
request, obj=obj, formfield_callback=formfield_callback, **kwargs)
return result

Categories