Modify presentation of element in a TabularInline - python

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.

Related

Fill a ChoiceField in Django template with external data

I'm new to Django and I'm having a hard time understanding forms when the data to choose from are not taken from the database nor user input that they're generated on the go.
I currently have a template with a single ChoiceField. The data inside this field aren't fixed and they're calculated on the go once the page is requested. To calculate it I need the username of the User who is logged in. Basically, the calculation returns a list of lists in the form of ((title, id),(title,id),(title,id)), etc. that I need to put into the ChoiceField to make the User choose from one of the options.
Now, I'm not understanding how to pass the calculated list of lists to the form. I've tried to add the calculations inside the form as below but it is clearly the wrong way.
The main issue is that, to calculate my list of lists, I need the request value, and I don't know how to access it from the form.
Another idea was to add the generate_selection function inside the init but then I don't know how to pass main_playlist to being able to add it to ChoiceField
Below my not working forms.py
forms.py
class ChoosePlaylistForm(forms.Form):
playlists = forms.ChoiceField(choices=HERE_SHOULD_GO_main_playlist)
def generate_selection(self):
sp_auth, cache_handler = spotify_oauth2(self.request)
spotify = spotipy.Spotify(oauth_manager=sp_auth)
user_playlists = spotify.current_user_playlists(limit=10)
main_playlist = []
for playlists in user_playlists["items"]:
playlists_list = []
playlists_list.append(playlists['name'])
playlists_list.append(playlists['id'])
main_playlist.append(playlists_list)
return main_playlist
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(ChoosePlaylistForm, self).__init__(*args, **kwargs)
class Meta:
model = User
fields = ('playlists',)
The views should be something like below so I'm able to pass the request
views.py
form = ChoosePlaylistForm(request=request)
Maybe overriding the field choices in the form constructor would work:
class ChoosePlaylistForm(forms.Form):
playlists = forms.ChoiceField(choices=())
class Meta:
model = User
fields = ('playlists',)
def __init__(self, *args, request=None, **kwargs):
super(ChoosePlaylistForm, self).__init__(*args, **kwargs)
self.request = request
self.fields['playlists'].choices = self.generate_selection()
def generate_selection(self):
sp_auth, cache_handler = spotify_oauth2(self.request)
spotify = spotipy.Spotify(oauth_manager=sp_auth)
user_playlists = spotify.current_user_playlists(limit=10)
choices = []
for playlist in user_playlists["items"]:
playlist_choice = (playlist["name"], playlist["id"])
choices.append(playlist_choice)
return choices

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.

How to extend CheckboxInput for a list of checkboxes

I am currently using a MultiCheckboxField like this:
class MultiCheckboxField(SelectMultipleField):
"""
A multiple-select, except displays a list of checkboxes.
Iterating the field will produce subfields, allowing custom rendering of
the enclosed checkbox fields.
"""
widget = widgets.ListWidget(prefix_label=False)
option_widget = widgets.CheckboxInput()
to generate a list of checkboxes. I would like to extend this list in such a way as to allow some of the list entries to have an associated TextInput field. When the box is checked, the corresponding text input is required.
I am new to Flask and WTForms, and I am having some trouble trying to figure out just how to attack the problem. I would be grateful for any suggestions that might provide some kind of direction.
See the FieldList and FormField with custom widgets
http://wtforms.readthedocs.org/en/latest/fields.html#field-enclosures
You can use a custom validator like this:
class RequiredIfChoice(validators.DataRequired):
# a validator which makes a field required if
# another field is set and has a truthy value
def __init__(self, other_field_name, desired_choice, *args, **kwargs):
self.other_field_name = other_field_name
self.desired_choice = desired_choice
super(RequiredIfChoice, self).__init__(*args, **kwargs)
def __call__(self, form, field):
other_field = form._fields.get(self.other_field_name)
if other_field is None:
raise Exception('no field named "%s" in form' % self.other_field_name)
for value, label, checked in other_field.iter_choices():
if label == self.desired_choice and checked:
super(RequiredIfChoice, self).__call__(form, field)
and in your form:
class MyForm(Form):
"""
Your form.
"""
multi = MultiCheckboxField('Multibox', choices=[(1, 'First'), (2, 'Second')], coerce=int)
multitext = StringField('SubText', [RequiredIfChoice('multi', 'Second')])
For a slightly similar question look at this Q&A.

How to initialize django-form data type field from a given choices?

First of all: I am not able to find out the proper Title of this question.
Anyhow the question is:
I have to fill a form at template and the fields of this form are user dependent. For example you passes integer (integer is not a datatype) as a parameter to the method and it should returns like this:
fileds = forms.IntegerField()
If you pass bool then it should like this:
fields = forms.BooleanField()
So that i can use them to create my form. I tried with this code but it returns into the form of string.
Some.py file:
choices = (('bool','BooleanField()'),
('integer','IntegerField()'))
def choose_field():
option = 'bool' # Here it is hardcoded but in my app it comes from database.
for x in choices:
if x[0]==option:
type = x[1]
a = 'forms'
field = [a,type]
field = ".".join(field)
return field
When i print the field it prints 'forms.BooleanField()'. I also use this return value but it didn't work. Amy solution to this problem?
The simpliest way is to create your form class and include fields for all possible choices to it. Then write a constructor in this class and hide the fields you don't want to appear. The constructor must take a parameter indicating which fields do we need. It can be useful to store this parameter in the form and use it in clean method to correct collected data accordingly to this parameter.
class Your_form(forms.ModelForm):
field_integer = forms.IntegerField()
field_boolean = forms.BooleanField()
def __init__(self, *args, **kwargs):
option = kwargs["option"]
if option == "integer":
field_boolean.widget = field_boolean.hidden_widget()
else:
field_integer.widget = field_integer.hidden_widget()
super(Your_form, self).__init__(*args, **kwargs)
In your controller:
option = 'bool'
form = Your_form(option=option)

django: exclude certain form elements based on a condition

I have some form fields that I want to include/exclude based on whether or not a certain condition is met. I know how to include and exclude form elements, but I am having difficulty doing it when I want it elements to show based on the outcome of a function.
Here is my form:
class ProfileForm(ModelForm):
# this_team = get Team instance from team.id passed in
# how?
def draft_unlocked(self):
teams = Team.objects.order_by('total_points')
count = 0
for team in teams:
if team.pk == this_team.pk:
break
count += 1
now = datetime.datetime.now().weekday()
if now >= count:
# show driver_one, driver_two, driver_three
else:
# do not show driver_one, driver_two, driver_three
class Meta:
model = Team
What I am trying to accomplish is, based on the standings of total points, a team should not be able to change their driver until their specified day. As in, the last team in the standings can add/drop a driver on Monday, second to last team can add/drop on Tuesday, and so on...
So the first problem -- how do I get the Team instance inside the form itself from the id that was passed in. And, how do I include/exclude based on the result of draft_unlocked().
Or perhaps there is a better way to do all of this?
Thanks a lot everyone.
This is actually fairly straightforward (conditional field settings) - here's a quick example:
from django.forms import Modelform
from django.forms.widgets import HiddenInput
class SomeForm(ModelForm):
def __init__(self, *args, **kwargs):
# call constructor to set up the fields. If you don't do this
# first you can't modify fields.
super(SomeForm, self).__init__(*args, **kwargs)
try:
# make somefunc return something True
# if you can change the driver.
# might make sense in a model?
can_change_driver = self.instance.somefunc()
except AttributeError:
# unbound form, what do you want to do here?
can_change_driver = True # for example?
# if the driver can't be changed, use a input=hidden
# input field.
if not can_change_driver:
self.fields["Drivers"].widget = HiddenInput()
class Meta:
model = SomeModel
So, key points from this:
self.instance represents the bound object, if the form is bound. I believe it is passed in as a named argument, therefore in kwargs, which the parent constructor uses to create self.instance.
You can modify the field properties after you've called the parent constructor.
widgets are how forms are displayed. HiddenInput basically means <input type="hidden" .../>.
There is one limitation; I can tamper with the input to change a value if I modify the submitted POST/GET data. If you don't want this to happen, something to consider is overriding the form's validation (clean()) method. Remember, everything in Django is just objects, which means you can actually modify class objects and add data to them at random (it won't be persisted though). So in your __init__ you could:
self.instance.olddrivers = instance.drivers.all()
Then in your clean method for said form:
def clean(self):
# validate parent. Do this first because this method
# will transform field values into model field values.
# i.e. instance will reflect the form changes.
super(SomeForm, self).clean()
# can we modify drivers?
can_change_driver = self.instance.somefunc()
# either we can change the driver, or if not, we require
# that the two lists are, when sorted, equal (to allow for
# potential non equal ordering of identical elements).
# Wrapped code here for niceness
if (can_change_driver or
(sorted(self.instance.drivers.all()) ==
sorted(self.instance.olddrivers))):
return True
else:
raise ValidationError() # customise this to your liking.
You can do what you need by adding your own init where you can pass in the id when you instantiate the form class:
class ProfileForm(ModelForm):
def __init__(self, team_id, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
this_team = Team.objects.get(pk=team_id)
teams = Team.objects.order_by('total_points')
count = 0
for team in teams:
if team.pk == this_team.pk:
break
count += 1
now = datetime.datetime.now().weekday()
if now >= count:
# show driver_one, driver_two, driver_three
else:
# do not show driver_one, driver_two, driver_three
class Meta:
model = Team
#views.py
def my_view(request, team_id):
profile_form = ProfileForm(team_id, request.POST or None)
#more code here
Hope that helps you out.

Categories

Resources