I want to display an extra field based on a boolean check associated with the model.
if obj.boolean:
exclude(self.extra_field)
But the issue with this is that the extra field is not associated with the model so it is throwing error stating model does not contain this extra field.
The output that i am looking for is that, when this boolean is true the extra field should not get displayed in the model admin as well as model inline. But when it is false it should get displayed.
How can i achieve this?
If that's only for display, you need to define a #property on your model which will return something depending on your boolean. Or you may define a method on an admin class like this:
def my_method(self, obj):
# return some value depending on obj.boolean
return ...
my_method.short_description = 'A label for my_method'
Than you may use it in admin's list_display list. I don't think you may completely remove field from list display for some entries and leave it for others (as it is table), but you may render it empty depending on your boolean.
For inlines you need to add this field into both fields and readonly_fields list to avoid Unknown field(s) error.
To display the field in detailed view, you need to add it to admin's readonly_fields.
In both ModelAdmin and InlineAdmin you may override get_readonly_fields method to return different fields-lists for different objects depending on your boolean.
Also, admin classes have get_fields method which is also overridable, but since your field is readonly you probably don't need it.
See ModelAdmin's options and methods for more details.
Related
I have a form (ModelForm) in Django, where I am adding a field for users in the init method as so:
self.fields["users"] = forms.ModelMultipleChoiceField(
queryset=users, widget=forms.CheckboxSelectMultiple, required=False,label="Add Designer(s)"
)
In the save method how I can iterate over the queryset for this field, however, I do not know how I can test if the particular model has been selected/checked. Help, please.
EDIT:
Let's say that you have a form where you want to be able to add users to a certain project, I set the users field as above (also usedMultipleChoiceField) but my real question is how do you determine the state of those checkboxes (which users should be added)?
Managed to fix it using MultipleChoiceField instead of ModelMultipleChoiceField. Then populated the choices with existing event IDs and passed it to the template.
In forms:
choices = forms.MultipleChoiceField(widget = forms.CheckboxSelectMultiple())
In views:
form.fields['choices'].choices = [(x.eventID, "Event ID: " + x.eventID) for x in unapproved]
Had to change some of the logic for finding and editing Event objects too.
The Django documentation states that a ModelMultipleChoiceField normalizes to a QuerySet of model instances. That means in your example, it will only return the users that have been checked. If none have been checked, it will return an empty QuerySet.
If you are overriding your ModelForm save method, you could include something like this:
selected_users = self.cleaned_data.get('users')
for user in selected_users:
project_users.add(user)
In Django's admin, you can add instance method calls to an edit page via the readonly option.
Can I do something similar with a ModelForm, and display the results of an instance method call? Preferably making it part of the forms visible_fields list.
My templates are quite generic so they are looping through the forms visible fields list and I would prefer not to alter these.
Oke, my solution will be quite hacky, but you could maybe do something like this:
class YourModelForm(forms.ModelForm):
class Meta:
model = YourModel
fields = []
def __init__(self, *args, *kwargs):
super(YourModelForm, self).__init__(*args, **kwargs)
# You can also user insert, to add on a certain position
self.visible_fields.append(self.instance.method())
Now a problem could be that you append a value, because I don't know how you render your fields. But you could fix this by append a Field-like object, which returns escaped (and saved) html on the necessary methods you call.
Other hacky option, add an additional field, set with an widget its attributes to disabled=disabled, and since a disabled input value isn't submitted with the form, set it required=False.
Say I have a model User, which has a credits field (IntegerField). When a user registers, I will set the credits field to 0, and I will update the credits for certain events.
I don't want the user know there is a field like this in the db table.
What attribute should I set to the field?
To accomplish the defaulting to 0 part, you can simply use the default argument of the model field.
For the part where you don't want your users to know about the field, you have a couple choices.
Solution 1: Field.editable
Defining your field as follows will cause the field to never show up in a model form.
credits = models.IntegerField(default=0, editable=False)
Downsides
You won't be able to edit the field's value in the admin
Form validation will never take this field into account (e.g., def clean_credits(self): won't run)
Solution 2: ModelForm.exclude|fields
Creating a ModelForm for the model is something you're going to be doing. You can define an exclude attribute on the form's Meta class, and add "credits" to the list. See the docs linked above. You can instead define fields on the Meta class, and omit "credits". The latter of the two options is considered a better practice, particularly when pertaining to security, and is known as a whitelist.
Downsides
You have to remember to define exclude or fields on every exposed form
Updating the "secret" field
The proper way to handle specifying a "secret" field's value when the field isn't in the form is:
# Inside your view's post method (or FormView.form_valid, if you're using generic views)
instance = form.save(commit=False) # Does everything except INSERT into the database
instance.credits = <however many credits you feel like giving the user>
instance.save()
If you didn't do that, and instead just saved the form as-is, the value specified by default would be set to the instance's credits field.
You'll want to use an IntegerField with default=0: credits = models.IntegerField(default=0). Just take care not to show this field to the user in any forms or when displaying the user.
E.g., if you had a ModelForm for User, do not include credits in the fields field of Meta
I'm using CreateView and UpdateView directely into urls.py of my application whose name is dydict. In the file forms.py I'm using ModelForm and I'm exluding a couple of fields from being shown, some of which should be set when either creating or updating. So, as mentioned in the title, update part works but create part doesn't which is obvious because required fields that I have exluded are sent empty which is not allowed in my case. So the question here is, how should I do to fill exluded fields into the file forms.py so that I don't have to override CreateView?
Thanks in advance.
Well, you have to set your required fields somewhere. If you don't want them to be shown or editable in the form, your options are to set them in the view (by using a custom subclass of CreateView) or if appropriate to your design in the save method of the model class. Or declare an appropriate default value on the field in the model.
It would also work to allow the fields into the form, but set them to use HiddenInput widgets. That's not safe against malicious input, so I wouldn't do that for purely automated fields.
You cannot exclude fields, which are set as required in the model definition. You need to define blank=True/null=True for each of these model fields.
If this doesn't solve your issue, then please show us the model and form definitions, so we know exactly what the code looks like.
I want to define a setter function for a Django model field in order to validate the input and immediately raise a error if value is not valid.
I don't think that doing the validation in save() or pre_save() is a feasible solution for my problem because of things beyond my control.
Also changing the field into a property and use plain field.getter and field.setter are not feasible as this will break .filter(), .exclude() etc on the field, plus it will require a scheme change even if I changed field to _field and overrode the manager.
So, am I forced to override __setattr__ or is there a better way of doing this?
You could subclass the Field and override to_python.
class UnforgivingCharField(CharField):
__metaclass__ = models.SubfieldBase
def to_python(self, value):
if not passes_my_test(value):
raise Hell()
return super(UnforgivingCharField, self).to_python(value)
This is used when the value is set and also during get_prep_value, etc., so it will catch invalid values when filtering.
More on this here. Note the line at the bottom of the linked section that tells us to_python will be called.
Edit: Just in case you happen to be using South, you will need to add some introspection rules if you implement custom model fields.
See this django ticket:
https://code.djangoproject.com/ticket/3148
Also, see here for discussion:
Django model fields getter / setter
And this blog post:
http://www.b-list.org/weblog/2006/aug/18/django-tips-using-properties-models-and-managers/
Also note there is the option of using a custom manager that translates your filter kwargs for you, and using _name for fields, and making your own #name.setter #name.getter etc.
Can you just define a function that provides the proper functionality and raises the correct error?
class YourModel(models.Model):
def add_your_value(self, value):
# check if value is correct
# raise exception if it isn't
# set correct property