Any way to pass a variable into a django meta class? - python

I am working on a django app that lets the user create a point on a map. This map needs to be centered at a certain location for ease of use, and this location is variable. I am using a LeafletWidget to let the user create this point.
The map can be centered by changing the attributes of the LeafletWidget. Unfortunately this widget is defined inside a Meta class, and as far as I understand you cannot pass a variable into it. Here an example of what I would like to do, somehow passing the variable center into the Meta class. center is a tuple with latitude and longitude values. This example code does not work of course. I don't know if you can somehow pass a variable into a Meta class.
class PointModelForm(ModelForm):
class Meta(center):
model = PointModel
fields = ['name', 'point']
widgets = {'point': LeafletWidget(attrs={'settings_overrides': {'DEFAULT_CENTER': center} })}
My best bet was defining the widgets attribute in the __init__ function, however Meta classes cannot access these attributes if I am correct. When I run this no widgets are used.
class PointModelForm(ModelForm):
class Meta:
model = PointModel
fields = ['name', 'point']
def __init__(self, center):
super(PointModelForm, self).__init__()
self.widgets = {'point': LeafletWidget(attrs={'settings_overrides': {'DEFAULT_CENTER': center} })}
Do you have any idea how to do this? Thanks in advance!

You can specify the attrs of a widget with:
class PointModelForm(ModelForm):
class Meta:
model = PointModel
fields = ['name', 'point']
def __init__(self, *args, center=None, **kwargs):
super(PointModelForm, self).__init__(*args, **kwargs)
if center is not None:
attrs = self.fields['point'].widget.attrs
subattrs = attrs.setdefault('settings_overrides', {})
subattrs['DEFAULT_CENTER'] = center
You can then construct a PointModelForm with:
PointModelForm(center=(55, 12)) # for GET request
PointModelForm(request.POST, request.FILES, center=(55, 12)) # for POST request

Related

Django Formset - each form with different initial value from M2M-through relationship

I have to models which are connected by a M2M-Field realized by another Class ComponentInModule, so that I can add there the extra information, how often a component is in the module.
class Module(models.Model):
...
component = models.ManyToManyField(Component, through="ComponentInModule")
class Component(models.Model):
...
class ComponentInModule(models.Model):
module = models.ForeignKey(InfrastructureModule, on_delete=models.CASCADE)
component = models.ForeignKey(InfrastructureComponent, on_delete=models.CASCADE)
amount = models.IntegerField(default=1)
Now I am trying to load a Module as a form with its corresponding Components as a formset.
class ComponentForm(ModelForm):
amount = IntegerField()
module = InfrastructureModule.objects.get(id=x)
ComponentFormSet = modelformset_factory(Component, form=ComponentForm, extra=0)
component_formset = ComponentFormSet(queryset=module.get_components())
As you can see my ComponentForm has the extra field for the amount. The question now is, how can I pass the value of amount to the Formset on creation, so that all forms are initialized with the right value? With a single Form it's no problem, because I can just pass the value to the __init__ function of the form and put it into the amount field self.fields["amount"].initial = amount. I tried passing a list of values to the formset with form_kwargs, but then I got the problem, that in the __init__function I dont know which of the values in the list is the right one right now.
Is there any way to do this using formsets? Or is there some other option I am missing how you can include the extra fields from a M2M-relation in a ModelForm?
So I worked it out. I made a custom BaseModelFormSet class:
class BaseCompFormset(BaseModelFormSet):
def get_form_kwargs(self, index):
kwargs = super().get_form_kwargs(index)
amount = kwargs["amount"][index]
return {"amount": amount}
Adjusted the __init__ function of the form:
def __init__(self, *args, **kwargs):
amount = kwargs.pop("amount")
super(ComponentForm, self).__init__(*args, **kwargs)
if self.instance:
self.fields["amount"].initial = amount
And used those to create my modelformset_factory:
amounts = [x.amount for x in module.get_components_in_module()]
ComponentFormSet = modelformset_factory(Component, formset=BaseCompFormset, form=ComponentForm, extra=0)
component_formset = ComponentFormSet(queryset=module.get_components(), form_kwargs={'amount':amounts})
And now succesfully got the forms of the formset with the right initial value for amount!

Django-CMS Child plugin to show filtered data from DB table

I have two plugins ProductSelector(parent) and SpecificationSelector(child). I want to set the child up so that when you add it to the parent the Specifications that are shown are the only ones for the product (parent). Right now it pulls in all the specifications from the table. These lines let me filter the data to get what I want.
edit: I found an error that i fixed in the code. I had the PluginBase names the same as the model. This allowed me to use ProductSelector.objects.get(cmsplugin_ptr=instance.parent) in the child to get the parent instance. I still need to figure out how to pass the filtered specification list to the "PluginAdmin Interface"
product = ProductSelector.objects.get(cmsplugin_ptr=instance.parent)
specification = Specifications.objects.filter(product_name__product_name__iexact = product.product_name)
However, I haven't figured out how to send that filtered list to the plugin admin interface.
class ProductSelectorPlugin(CMSPluginBase):
model = ProductSelector
name = "Product Selector"
render_template = "product_selector.html"
allow_children = True
child_classes = ['SpecificationSelectorPlugin']
def render(self, context, instance, placeholder):
context['instance'] = instance
return context
plugin_pool.register_plugin(ProductSelectorPlugin)
class SpecificationSelectorPlugin(CMSPluginBase):
model = SpecificationSelector
render_template = "specification_selector.html"
formfield_overrides = {models.ManyToManyField: {'widget': CheckboxSelectMultiple},}
def render(self, context, instance, placeholder):
product = ProductSelector.objects.get(cmsplugin_ptr=instance.parent)
specification = Specifications.objects.filter(product_name__product_name__iexact = product.product_name)
context['instance'] = instance
return context
plugin_pool.register_plugin(SpecificationSelectorPlugin)
models.py
class ProductSelector(CMSPlugin):
product_name = models.ForeignKey(Product, help_text = "Select the product you want to place")
new_product = models.BooleanField(blank=True)
class SpecificationSelector(CMSPlugin):
specification = models.ManyToManyField(Specifications, blank=True)
def __unicode__(self):
return unicode(self.specification)
Here is an screenshot the Django-cms plugins in the placeholder. Currently it is showing all specs in the table, but I just want it to be the specs for that particular product.
http://imgur.com/3R1LobC
Thank you in advance for the help.
CMSPluginBase inhertis from ModelAdmin which means that you can override the form rendered when adding and editing your plugin.
So you can create a ModelForm subclass like so:
class SpecificationSelectorPluginForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SpecificationSelectorPluginForm, self).__init__(*args, **kwargs)
if self.instance.parent_id:
# Assume that the parent is a product instance
parent_plugin = self.instance.parent
product = parent_plugin.get_plugin_instance()[0]
if product:
# It's possible that product is an orphan plugin.
specifications = Specifications.objects.filter(
product_name__product_name__iexact=product.product_name)
self.fields['specification'].queryset = specifications
then change your SpecificationSelectorPlugin to use this form like so:
class SpecificationSelectorPlugin(CMSPluginBase):
form = SpecificationSelectorPluginForm
The above will only work if the specification plugin is a direct child of the product plugin.

Modify presentation of element in a TabularInline

For this particular administration page, I'd like to turn the 'current value' (outlined in a red circle) into a link going back to the administration page for this particular object.
But I can't find where to go to make this change. I know that I need to somehow override how this
is displayed but I can't figure it out.
What do I need to override to do what I want?
Admin model definition:
class FirmwareConfigElementsChoiceInline(admin.TabularInline):
model = FirmwareConfigElements
extra = 1
class FirmwareConfigAdmin(admin.ModelAdmin):
save_as = True
list_display = ('name', 'description')
inlines = [FirmwareConfigElementsChoiceInline]
Using Filip's great help I've gotten to this:
class FirmwareConfigElementsForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
klass = FirmwareConfigElementsForm
super(klass, self).__init__(*args, **kwargs)
if self.instance.type == 'incfw':
value = self.instance.value
url = '#' # TODO: get the URL for the value
hyperlink = '%s' % (url, value)
label = self.fields['type'].label.replace(value, hyperlink)
self.fields['type'].label = label
But in the above code, self.fields['type'].label has the contents Type and not Include another FW Config - BASE:IBM-HS22/HS22V as I was expecting.
I've explored it in the debugger but I can't figure out how to get to the particular label that I want to change.
Inline admin models have a template property you can use to supply a custom template. From there, you'll need to modify the code to add the url.
You'll need to provide a custom ModelForm for the FirmwareConfigElements model, which you'll set as the value for the FirmwareConfigElementsChoiceInline.form class attribute.
Here you'll want to override the ModelForm.__init__() instance method to assign a new label for the field you want to override if the form is bound:
class FirmwareConfigElementsForm(models.ModelForm):
def __init__(self, *args, **kwargs):
klass = FirmwareConfigElementsForm
super(klass, self).__init__(*args, **kwargs)
if form.is_bound and 'value' in self.data:
value = self.data['value']
url = '' # TODO: get the URL for the value
hyperlink = '%s' % (url, value)
label = self.fields['type'].label.replace(value, hyperlink)
self.fields['type'].label = label
class FirmwareConfigElementsChoiceInline(admin.TabularInline):
model = FirmwareConfigElements
extra = 1
form = FirmwareConfigElementsForm
Now, if you want the label to change dynamically as the user changes the form data, then it gets a lot uglier and you'll have to resort to referencing JavaScript media and performing the above on the fly.

Django Dynamic Class

These 2 methods are equivalent.
method 1
class X(object):
a = 1
method 2
X = type('X', (object,), dict(a=1))
I want to know what is the equivalent of :
class ObjectTable(tables.ModelTable):
id = tables.Column(sortable=False, visible=False)
societe = tables.Column(sortable=False, visible=False)
class Meta:
model = models.get_model('core', "Fournisseur")
I tried this but don't work :
ObjectTable=type('ObjectTable',(tables.ModelTable,),dict(model=myModel))
ObjectTable=type('ObjectTable',(tables.ModelTable,),dict(meta.model=myModel))
ObjectTable=type('ObjectTable',(tables.ModelTable,),dict(meta=myModel))
Thanks.
This is the solution :
def CreateForm(for_model, request=None, instance=None, user=None):
class _StateMachineBaseModelForm(ModelForm):
class Meta:
model = for_model
exclude = ('societe',)
def __init__(self, *args, **kwargs):
super(_StateMachineBaseModelForm, self).__init__(*args, **kwargs)
try:
if user:
self.fields['banque'].queryset = Banque.objects.filter(pays=user.get_profile().societe.pays)
except:
pass
if for_model: return _StateMachineBaseModelForm(request, instance=instance)
It's the exact same thing with the values that you find in your django example. Try it for yourself.
All of your examples "do not work" as you put it, because (a) you don't create any fields other than meta (b) that should be spelled Meta (c) the value of Meta should be an (old style) class.
Before we start, I agree with S. Lott in the comments. You almost certainly don't want to do this.
Here goes:
# You can't create an old-style class with type,
# so define a function that creates one.
def make_meta_class():
class Meta:
model = models.get_model('core', "Fournisseur")
return Meta
# Create the dictionary (remember to include the fields as well as the meta class)
d=dict(,
Meta=make_meta_class(),
id=tables.Column(sortable=False, visible=False)
societe=tables.Column(sortable=False, visible=False)
)
# Create the class dynamically
ObjectTable=type('ObjectTable', (tables.ModelTable,), d)

Django form field label translations

I have a baseform with over 20 fields. Then I have about 15 other forms inheriting from that form, passing in a parameter called fields which the baseform uses to delete all other fields. Best explain via example:
class BaseForm(forms.Form):
reportid = forms.HiddenInput()
fromdate = forms.DateField(label=_("From"), widget=widgets.AdminDateWidget())
todate = forms.DateField(label=_("To"), widget=widgets.AdminDateWidget())
sort_by = forms.ChoiceField(label=_("Sort by"), choices=[])
.......
def __init__(self, *args, **kwargs):
fields = kwargs.pop('fields')
#Pseudo:
***del self.fields[field] for field not in fields***
class SubForm(forms.Form):
def __init__(self, *args, **kwargs):
fields = ['reportid', 'todate']
super(SubForm, self).__init__(fields=fields, *args, **kwargs)
The resulting form would then look like this:
class SubForm(forms.Form):
reportid = forms.HiddenInput()
todate = forms.DateField(label=_("To"), widget=widgets.AdminDateWidget())
My problem is that when the BaseForm is initialized for the first time, the labels are bound to the fields with the active language, and when another user logs in with another language setting (or the current user changes languages) the field labels don't update.
I've come to a solution using a dict like this:
labels = {
'todate': lambda: _("To"),
'fromdate': lambda: _("From"),
.....
}
and then when initializing the baseform looping through all fields and setting
self.fields[field].widget.label = labels[field]()
Do I have any nicer (read: more pythonic) way of achieving this?
Django provides _lazy variants of the translation functions (for example ugettext_lazy) so you can ark strings for translations at the access time (as opposed to when the translation function is called).
It's documented in details at https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#lazy-translation

Categories

Resources