Django forms: Defining model in Meta on the fly - python

I have following form:
class EmailPreferences(forms.ModelForm):
""" Base form used for fields that are always required """
def __init__(self, *args, **kw):
super(EmailPreferences, self).__init__(*args, **kw)
class Meta:
model = teacher_email_preferences
exclude = ['id', 'c_user']
def save(self, force_insert=False, force_update=False, commit=True):
obj = super(EmailPreferences, self).save(commit=commit)
return obj
As you can see model name is fixed which is teacher_email_preferences. But my site has two types of user one is Teacher other is Student. I do not want to create a separate form. So i want to change model to student_email_preferences when initiating this form. How can I pass a model here when doing form = EmailPreferences()?

You can't. But you can use a closure instead.
def emailform(emailmodel):
class EmailPreferences(forms.ModelForm):
...
class Meta:
model = emailmodel
...
return EmailPreferences
...
form = emailform(teacher_email_preferences)()

Related

Django save default value in Proxy Model

Different proxy models should be different in type.
If I query those models I the right ones.
I am trying to save a default type field in a proxy model.
I don't want to set it everytime in the view.
This does not work. The type field is always "TYPE1".
models.py:
class MyModel(models.Model):
class ModelType(models.TextChoices):
TYPE1 = 'TYPE1', _('TYPE1')
TYPE2 = 'TYPE2', _('TYPE2')
type = models.CharField(max_length=100, choices=ModelType.choices, default='TYPE1')
class Type2Manager(models.Manager):
def get_queryset(self):
return super(Type2Manager, self).get_queryset().filter(type='TYPE2')
def save(self, *args, **kwargs):
kwargs.update({'type': 'TYPE2'})
return super(Type2Manager, self).save(*args, **kwargs)
class Type2ProxyModel(MyModel):
class Meta:
proxy = True
objects = Type2Manager()
views.py:
def create_type2_model(request):
form = Type2Form(request.POST, initial={})
f = form.save(commit=False)
f.save()
forms.py:
class Type2Form(ModelForm):
class Meta:
model = Type2ProxyModel
Update 25.02.2020 12:18:
I found out that this sets the correct type. But I don't know how to use create() in a ModelForm.
class Type2Manager(models.Manager):
...
def create(self, **kwargs):
kwargs.update({'type': 'TYPE2'})
return super(Type2Manager, self).create(**kwargs)
Type2ProxyModel.objects.create()
A model manager operates on a "table-level". When you create an object via a form it uses the model objects and not the model manager and thus you'd need to override the save of your proxy model. If I modify your Type2ProxyModel to this it works:
class Type2ProxyModel(MyModel):
class Meta:
proxy = True
objects = Type2Manager()
def save(self, *args, **kwargs):
self.type = 'TYPE2'
return super(Type2ProxyModel, self).save(*args, **kwargs)

Django Admin - Filter ManyToManyField with through model

How can I filter a queryset inside the Admin page of an object that has a ManyToManyField relation with a manually defined through model?
Given models.py
class Foo(models.Model):
foo_field1 = models.CharField(max_length=50)
class Main(models.Model):
main_field1 = models.CharField(max_length=50)
m2mfield = models.ManyToManyField(Foo, through="FooBar")
class FooBar(models.Model):
main = models.ForeignKey(Main, on_delete=models.CASCADE)
foo = models.ForeignKey(Foo, on_delete=models.CASCADE)
new_field = models.CharField(max_length=50)
Inside admin.py
class M2MInlineAdmin(admin.TabularInline):
model = Main.m2mfield.through
extra = 1
class MainAdmin(admin.ModelAdmin):
inlines = [M2MInlineAdmin,]
...
def formfield_for_manytomany(self, db_field, request, **kwargs):
print('called formfield_for_manytomany')
return super().formfield_for_manytomany(db_field, request, **kwargs)
def get_field_queryset(self, db, db_field, request):
print('called get_field_queryset')
return super().get_field_queryset(db, db_field, request)
I try to access both of these methods, but none of them are called if I specify a through table. However, they do get called if the ManyToMany relation is simply defined as like this:
class Main(models.Model):
main_field1 = models.CharField(max_length=50)
m2mfield = models.ManyToManyField(Foo)
Is there a method to filter the queryset when a through table is specified (while being able to access the request context)?
EDIT:
The methods are indeed called when the ManyToManyField has a through model specified, only if there are no fieldsets specified inside the modelAdmin class.
How to access these methods when fieldsets are defined?
formfield_for_manytomany method seems to be called only when default form is used. When fieldsets is defined, it is using a different form which is why above method is not getting called.
Since you are using tabular admin for many to many field, you can override get_queryset to filter with field.
class M2MInlineAdmin(admin.TabularInline):
model = Main.fruits.through
extra = 1
def get_queryset(self, request):
qs = super(M2MInlineAdmin, self).get_queryset(request)
qs = qs.filter(some_arg=some_value)
return qs
Alternatively, you can write a custom model form and use it in admin instead of default form.
class MainAdminForm(forms.ModelForm):
class Meta:
model = Main
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# custom setup
class MainAdmin(admin.ModelAdmin):
form = MainAdminForm
You can use the formfield_for_foreignkey() method on the inline class.
class M2MInlineAdmin(admin.TabularInline):
model = Main.m2mfield.through
extra = 1
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "car":
kwargs["queryset"] = Car.objects.filter(owner=request.user)
return super().formfield_for_foreignkey(db_field, request, **kwargs)

Can I assign queryset to django form in views? [duplicate]

I am creating my form in Form.py like this:
class pdftabelModelForm(forms.ModelForm):
class Meta:
model = pdftabel_tool_
fields = ['apn', 'owner_name']
apn = forms.ModelChoiceField(queryset= Field.objects.values_list('name', flat=True), empty_label="(Choose field)")
owner_name = forms.ModelChoiceField(queryset= Field.objects.values_list('name', flat=True), empty_label="(Choose field)")
But due to some reasons like 'self' is not available in form.py. I can only access it in views.py. So I want to make it like
class FieldForm(ModelForm):
class Meta:
model = pdftabel_tool_
fields = (
'apn',
'owner_name',)
How can I make these fields as dropdown like I did in my forms.py?
Why are you set on doing it in views.py? forms.py is the appropriate place to do this.
Instead of redefining your fields, you should use the form's __init__ method to override the querysets for your fields, like so:
class pdftabelModelForm(forms.ModelForm):
class Meta:
model = pdftabel_tool_
fields = ['apn', 'owner_name']
def __init__(self, *args, **kwargs):
super(pdftabelModelForm, self).__init__(*args, **kwargs)
self.fields['apn'].queryset = X
self.fields['owner_name'].queryset = X
EDIT: if you need to pass extra parameters to your form, update the init method to this:
def __init__(self, *args, **kwargs):
self.layer_id = self.kwargs.pop('layer_id')
super(pdftabelModelForm, self).__init__(*args, **kwargs)
self.fields['apn'].queryset = X
self.fields['owner_name'].queryset = X
And when you initialize your form from views.py, pass the parameter:
form = pdftableModelForm(layer_id=X)

how to override django admin form Foreignkey based on request.user

admin.py
class PromoAdmin(admin.modelAdmin)
list_display = ( 'name', 'id', 'category', 'promo_type', 'store', 'brand', 'date_start' )
form = SampleForm
forms.py
class SampleForm(forms.ModelForm):
class Meta:
model = Promo
def __init__(self, request *args, **kwargs):
super(PromoAdminForm, self).__init__(*args, **kwargs)
self.fields["store"].queryset = Store.objects.filter(onwer=request.user)
got an error on request
Django Version: 1.3.1
Exception Type: TypeError
Exception Value:
init() takes at least 2 arguments (1 given)
You cannot initiate the store field with request.user in the field declaration. You can try the following:
class MyAwesomeForm(forms.ModelForm):
store = forms.ModelChoiceField(Store.objects)
class Meta:
model = Promo
def __init__(self, user, *args, **kwargs):
super(MyAwesomeForm, self).__init__(*args, **kwargs)
self.fields['store'].queryset = Store.objects.filter(owner=user)
While instantiating the form you can pass the request.user object.
myform = MyAwesomeForm(request.user)
If you want to achieve this in the admin you might try this
For providing only the objects related to the logged-in user in the admin provides the possibility to overwrite ModelAdmin.queryset function:
class MyModelAdmin(admin.ModelAdmin):
form = MyAwesomeAdminForm()
def queryset(self, request):
qs = super(MyModelAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(store__owner=request.user)
class MyAwesomeAdminForm(forms.ModelForm):
class Meta:
model = Promo
Note that store__owner only works if you have a foreign key field stored in your promo model as such:
class Promo(models.Model):
store = models.ForeignKey(Store)
class Store(models.Model):
owner = models.ForeignKey(User)
I assume it should also be possible to somehow pass the request to the init method of the form. But did not find a suitable approach to do it.

Getting the model ID from a Django form after having saved it

view.py
someForm = SomeForm(request.POST)
...
someForm.customSave(request.user)
forms.py
class SomeForm(ModelForm):
class Meta:
model = Some
def customSave(self,user):
lv = self.save(commit=False)
lv.created_by = user
lv.save()
How can I get the id of the model (or the model) I have just saved from someForm?
Since the behavior of ModelForm.save is to return the instance, you might want to return the instance in your customSave method
def customSave(self, user):
lv = self.save(commit=False)
lv.created_by = user
lv.save()
return lv
then you can access the pk or id on the instance
inst = someForm.customSave(request.user)
inst.pk or inst.id
Just use lv.pk or lv.id, after calling lv.save(). The id is set on the instance after it's saved.
in your Model class override the save method:
def save(self, form, *args, **kwargs):
super(MyModel, self).save(*args, **kwargs)
logger.debug(self.id)

Categories

Resources