Django Model instance value in ModelForm constructor - django

I would like to create a dynamic Form based on a ModelForm. The aim is to add fields with the information in the json field.
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['name', 'json']
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
[ Create fields here]
I can create the fields dynamically like this:
variables = ('var_1', 'var_2',)
for v in variables:
self.fields[v] = forms.CharField(label=v)
Now, I would like to replace variables with the json.variables value. I tried this: self.fields['json'].initial, self.fields['json'].data, self.fields['json'].cleaned_data without success.
Do you know how can I have access to the model value?

Finally the solution is quite easy. We just have to use self.instance.json.

Related

Django Admin overwrite the __str__ method in an autocomplete_field

I want to overwrite the __str__ method in Django admin when using the autocomplete_fields = () but the returned values are using __str__.
I have a form something like
class MyAdminForm(forms.ModelForm):
placement = forms.Select(
choices = Organisation.objects.active(),
)
class Meta:
model = Lead
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['placement'].label_from_instance = lambda obj: f'{str(obj)} {obj.post_code}'
This will provide back a Select with the organisation name and post code in the dropdown fields. But there are some 80k choices so I need to using autocomplete. Within within admin.py I have
class LeadAdmin(admin.ModelAdmin):
form = LeadAdminForm
autocomplete_fields = ('placement',)
As soon as I add the autocomplete_fields I lose my postcode and it reverts to just showing the __str__
Hoa can I used autocomplete_fields and overwrite the __str__ method?
This question is answered through Benbb96 comment above which I've copied here so I can close it
So maybe this answer can help you :
stackoverflow.com/a/56865950/8439435 – Benbb96

How can I set some value to inherited model class field in Django model?

I made one model(ModelA) in which 2 choices present there, I am inheriting this model, in the other two models
CHOICES = (("work", "work"), ("Home", "Home"))
class ModelA(models.Model):
type_of_address = models.CharField(choices=CHOICES)
...
class ForWorkModel(ModelA):
type_of_address--->work
class ForHomeModel(ModelA):
type_of_address--->Home
I want to inherit the model and want to set some field values, as I mentioned in the code.
Is there any way?
You could add a custom save on each of the individual models to set the desired string:
class ForWorkModel(ModelA):
def save(self, *args, **kwargs):
if not self.pk: # only change if the object is new
self.type_of_address = "work" # or CHOICES[0][0]
super().save(*args, **kwargs)
or, I'm not 100% sure, but this might work:
class ModelA(models.Model):
type_of_address = models.CharField(choices=CHOICES, default=cls.get_default_address)
class ForWorkModel(ModelA):
def get_default_address():
return "work"

Why does a queryset applied in a ModelForm not inherit a queryset from a ModelManager?

I have a custom queryset on a model manager:
class TenantManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(myfield=myvalue)
class TenantModel(TenantModelMixin, models.Model):
objects = TenantManager()
class Meta:
abstract = True
I use the abstract TenantModel as a mixin with another model to apply the TenantManager. E.g.
class MyModel(TenantModel):
This works as expected, applying the TenantManager filter every time MyModel.objects.all() is called when inside a view.
However, when I create a ModelForm with the model, the filter is not applied and all results (without the filter are returned. For example:
class AddPersonForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('person', )
Why is this and how to I ensure the ModelManager is applied to the queryset in ModelForm?
Edit
#Willem suggests the reason is forms use ._base_manager and not .objects (although I can not find this in the Django source code), however the docs say not to filter this kind of manager, so how does one filter form queries?
Don’t filter away any results in this type of manager subclass
This
manager is used to access objects that are related to from some other
model. In those situations, Django has to be able to see all the
objects for the model it is fetching, so that anything which is
referred to can be retrieved.
If you override the get_queryset() method and filter out any rows,
Django will return incorrect results. Don’t do that. A manager that
filters results in get_queryset() is not appropriate for use as a base
manager.
You can do it in two ways:
First: When creating the form instance, add the queryset for the desired field.
person_form = AddPersonForm()
person_form.fields["myfield"].queryset = TenantModel.objects.filter(myfield="myvalue")
Second: Override the field's queryset in the AddPersonForm itself.
class AddPersonForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('person', )
def __init__(self, *args, **kwargs):
super(AddPersonForm, self).__init__(*args, **kwargs)
self.fields['myfield'].queryset = TenantModel.objects.filter(myfield="myvalue")
I'm not sure why your code doesn't properly works. Probably you haven't reload django app. You could load queryset in __init__ of your form class
class AddPersonForm(forms.ModelForm):
person = forms.ModelMultipleChoiceField(queryset=None)
class Meta:
model = MyOtherModel
fields = ('person', )
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['person'].queryset = MyModel.objects.all()

add extra field to ModelForm

I am adding an extra field to a Django ModelForm like that:
class form(forms.ModelForm):
extra_field = forms.CharField(label='Name of Institution')
class Meta:
model = db_institutionInstitution
fields = ['conn_kind','time','inst_name2']
The form is actually working fine, but I cant prepopulate it. I use it in a modelformset_factory:
formset = modelformset_factory(db_institutionInstitution,form=form)
I manually run through a queryset and add the entry in the dictionary needed for the additional form in the formset. However, when I call:
formset1 = formset(prefix='brch',queryset=qs1)
the extra_field is not prepopulated as intended (the rest is working fine).
Can anyone help?
If you want to set a default.
extra_field = forms.CharField(label='Name of Institution', initial="harvard")
If you want to dynamically set a value put it on form initialization:
def __init__(self, *args, **kwargs):
super(form, self).__init__(*args, **kwargs)
self.fields['extra_field'].initial = "harvard"

How to modify form choices from class?

I have form class:
class Form(forms.ModelForm):
id = forms.ModelChoiceField(queryset=Option.objects.all(), widget=forms.HiddenInput())
category = forms.ModelChoiceField(queryset=Category.objects.all())
class Meta:
model = Option
fields = ('id', 'category')
def choices(self, ext_data):
# something with extdata...
choices = [('1','one')]
category = forms.ModelChoiceField(queryset=choices)
but this:
my_form.choices(something)
is not working. Why?
I must implement this in class because i have one view and many different forms. Each form have specific choices function.
First, queryset must be a queryset, not a list, since you're using ModelChoiceField. Second, to reference the category form field use self.fields['category']. Your function should thus look something like this:
def choices(self, ext_data):
#I'm not sure what ext_data is, but I suspect it's something to filter the Categories
self.fields['category'].queryset = Category.objects.filter(something=ext_data)
#If ext_data itself is a queryset you can use it directly:
self.fields['category'].queryset = ext_data
For clarification, a queryset is what you get when you use Model.objects.filter(xxx) or any other filtering action on your model.
Try to use init:
class MessageAdminForm(forms.ModelForm):
def __init__(self, *arg, **kwargs):
super(MessageAdminForm, self).__init__(*args, **kwargs)
# set choices this way
self.fields['field'].choices = [(g.id, g) for g in something]