I have a form class which takes a model, meta class of the form is as below,
the problem is that I want to make the patient_signature and worker_signature fields unrequired, I tried removing the class wide required_css_class but that did not help, giving each attribute classes required as True/False is also not helping.
Any suggestions...?
class Meta:
model = Locator
exclude = ('patient','worker', 'mode_of_transmission', 'secondary_telephone_number', 'locator', 'grant', 'thumbnail')
creation_date=forms.DateField(initial=datetime.date.today,
widget=SelectDateWidget(),
label="Creation Date")
patient_signature=forms.CharField(widget=ClientSignatureWidget())
worker_signature=forms.CharField(widget=WorkerSignatureWidget())
required_css_class = 'required'
Assuming you are talking about a ModelForm, you cannot override the fields inside the Meta class. It must be outside.
Also, if the field is required in the model but not in the form, then you must provide a default value, like this:
class LocatorForm:
patient_signature = forms.CharField(widget=forms.HiddenInput(), initial=" ")
class Meta:
...
Alternatively, do not mention that field in the fields list and set a value by overriding the submission of the form's POST.
patient_signature=forms.CharField(widget=ClientSignatureWidget(), required=False)
Related
I'm working on a project that has a Chapter, with each Chapter having a title, content, and order. I'd like to keep the field 'order' named as is, but have the field displayed in a CreateView as something else, like 'Chapter number'. The best information I've found recommends updating the "labels" attribute in the Meta class, but this isn't working for me.
This is what I'm using now, which doesn't work:
class ChapterCreate(CreateView):
model = models.Chapter
fields = [
'title',
'content',
'order',
]
class Meta:
labels = {
'order': _('Chapter number'),
}
I've also tried using the 'label's attribute outside of Meta, but that didn't work either. Should I be using a ModelForm instead, or is there a correct way to do this?
The simplest solution in this case would be to set the verbose_name for your model field
class Chapter(models.Model):
order = models.IntegerField(verbose_name= _('Chapter number'))
Note I have use IntegerField in this example, please use whatever type is required.
Even if it is an old subject, I think a way to do this now with Django 3.1 would be:
in views.py
class ChapterCreate(CreateView):
model = models.Chapter
form_class = ChapterForm
and in forms.py, define ChapterForm
class ChapterForm(ModelForm):
class Meta:
model = models.Chapter
fields = ('title', 'content','order')
labels = {
'order': _('Chapter number'),
}
If you want different values for the verbose_name of the model field and the user-facing label of the form field, the quickest way might be to override the get_form(…) method of CreateView:
class ChapterCreate(CreateView):
(...)
def get_form(self, form_class=None):
form = super().get_form(form_class)
form.fields['order'].label = _('Chapter number')
return form
I removed some fields from my model, but I want the serializer to still accept the fields as input. How do I have fields the serializer accepts but doesn't use?
class EventBaseSerializer(ModelSerializer):
class Meta:
model = models.Event
fields = ("id", "name")
#unused_fields = ("last_name")
From http://www.django-rest-framework.org/api-guide/serializers/
You can add extra fields to a ModelSerializer or override the default
fields by declaring fields on the class, just as you would for a
Serializer class.
class AccountSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
groups = serializers.PrimaryKeyRelatedField(many=True)`
class Meta:
model = Account
If you want a field to be used for input but not output, you'll need to add this field to the fields list and mark it as write_only likely with extra_kwargs
In my case, I want to get data other than the model has and use them in the serializer methods for something else. But the default "create" method of the serializer try to create the object using those foreign fields too and should give an error like:
Got a TypeError when calling MyModel.objects.create(). This may be because you have a writable field on the serializer class that is not a valid argument to MyModel.objects.create(). You may need to make the field read-only, or override the MyModelCreateSerializer.create() method to handle this correctly.
I fixed the issue by popping them from the serializer's data after using them as I want and everything continued just fine.
class MyModelCreateSerializer(serializers.ModelSerializer):
foreign_input_1 = serializers.DateField(write_only=True)
foreign_input_2 = serializers.DateField(write_only=True)
class Meta:
model = MyModel
fields = '__all__'
def validate(self, data):
MySecondModel.objects.create(foreign_input_1=data.pop('foreign_input_1'),
foreign_input_1=data.pop('foreign_input_2'))
return data
Don't forget to use the write_only=True parameter on the foreign fields. The serializer will try to read them from the object in any data return operation, like returning the value of the created object.
This is my Model:
class Post(models.Model):
user = models.ForeignKey(User)
post = models.CharField(max_length=400)
country = models.ForeignKey(Country, blank=True, null=True)
and this is my serializer:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('user', 'post', 'country',)
def create(self, validated_data):
post = Post(
user = User.objects.get(username='MyUser'),
post = validated_data['post'],
)
if validated_data.get('country', None):
post.country = validated_data['country']
return post
Is there any way for me to tell DRF that if the value of the field is null (because the 'country' field is optional and sometimes not provided) then to skip it and just serialize the other data? Or at least serialize it with a value of None?
I don't think I can use SerializerMethodField (http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield) because the 'country' field is not a read-only field (I write too it too, if it is provided).
I basically want to omit the field (or at least make the value None) when serializing an object If the field is null.
As of DRF 3.2.4, so long as you add
blank=True
to the models field like so:
class Post(models.Model):
country = models.ForeignKey(Country, blank=True)
then DRF will treat the field as optional when serializing and deserializing it (Note though that if there is no null=True on the model field, then Django will raise an error if you try to save an object to the database without providing the field).
See the answer here for more information: DjangoRestFramework - correct way to add "required = false" to a ModelSerializer field?
If you are using pre-DRF 3.2.4, then you can override the field in the serializer and add required=False to it. See the documentation here for more information on specifying or overriding fields explicitily: http://www.django-rest-framework.org/api-guide/serializers/#specifying-fields-explicitly
So something like this (Note that I did not fully test the code below but it should be something along these lines):
class PostSerializer(serializers.ModelSerializer):
country = serializers.PrimaryKeyRelatedField(required=False)
class Meta:
model = Post
fields = ('user', 'post', 'country',)
This thread might be useful:
https://stackoverflow.com/a/28870066/4698253
It basically says that you can override the to_representation() function with a slight modification.
I would have put this in the comments but I don't have enough points yet :(
Use allow_null=True:
allow_null - If set to True, the field will accept values of None or the empty string for nullable relationships. Defaults to False.
serializers.py
class PostSerializer(serializers.ModelSerializer):
tracks = serializers.PrimaryKeyRelatedField(allow_blank=True)
class Meta:
model = Post
I have a model without field 'test'. I'm assigning this field in runtime:
ability = Ability.objects.first()
ability.test = 'TEST!!'
I also have the serilizer:
class AbilitySerializer(serializers.ModelSerializer):
class Meta:
model = Ability
fields = ('name', 'test',)
And when I use it:
return Response(AbilitySerializer(ability).data)
I'm getting error:
Field Field name `test` is not valid for model `Ability`.
EDIT: I'm still facing this issue when I'm passing array of objects to serializer (with many=True). It's OK when I pass single instance.
Why and how to fix it?
As Ajay Gupta indicated, non-model fields/methods/properties must be explicitly declared:
class AbilitySerializer(serializers.ModelSerializer):
# read_only since test is not a model field
test = serializers.CharField(read_only=True)
class Meta:
model = Ability
fields = ('name', 'test',)
Additionally, if you do not always provide test, consider:
test = serializers.CharField(required=False, read_only=True)
I have a model registered on the admin site. One of its fields is a long string expression. I'd like to add custom form fields to the add/update pages of this model in the admin. Based on the values of these fields I will build the long string expression and save it in the relevant model field.
How can I do this?
I'm building a mathematical or string expression from symbols. The user chooses symbols (these are the custom fields that are not part of the model) and when they click save then I create a string expression representation from the list of symbols and store it in the DB. I don't want the symbols to be part of the model and DB, only the final expression.
Either in your admin.py or in a separate forms.py you can add a ModelForm class and then declare your extra fields inside that as you normally would. I've also given an example of how you might use these values in form.save():
from django import forms
from yourapp.models import YourModel
class YourModelForm(forms.ModelForm):
extra_field = forms.CharField()
def save(self, commit=True):
extra_field = self.cleaned_data.get('extra_field', None)
# ...do something with extra_field here...
return super(YourModelForm, self).save(commit=commit)
class Meta:
model = YourModel
To have the extra fields appearing in the admin just:
Edit your admin.py and set the form property to refer to the form you created above.
Include your new fields in your fields or fieldsets declaration.
Like this:
class YourModelAdmin(admin.ModelAdmin):
form = YourModelForm
fieldsets = (
(None, {
'fields': ('name', 'description', 'extra_field',),
}),
)
UPDATE:
In Django 1.8 you need to add fields = '__all__' to the metaclass of YourModelForm.
It it possible to do in the admin, but there is not a very straightforward way to it. Also, I would like to advice to keep most business logic in your models, so you won't be dependent on the Django Admin.
Maybe it would be easier (and maybe even better) if you have the two seperate fields on your model. Then add a method on your model that combines them.
For example:
class MyModel(models.model):
field1 = models.CharField(max_length=10)
field2 = models.CharField(max_length=10)
def combined_fields(self):
return '{} {}'.format(self.field1, self.field2)
Then in the admin you can add the combined_fields() as a readonly field:
class MyModelAdmin(models.ModelAdmin):
list_display = ('field1', 'field2', 'combined_fields')
readonly_fields = ('combined_fields',)
def combined_fields(self, obj):
return obj.combined_fields()
If you want to store the combined_fields in the database you could also save it when you save the model:
def save(self, *args, **kwargs):
self.field3 = self.combined_fields()
super(MyModel, self).save(*args, **kwargs)
Django 2.1.1
The primary answer got me halfway to answering my question. It did not help me save the result to a field in my actual model. In my case I wanted a textfield that a user could enter data into, then when a save occurred the data would be processed and the result put into a field in the model and saved. While the original answer showed how to get the value from the extra field, it did not show how to save it back to the model at least in Django 2.1.1
This takes the value from an unbound custom field, processes, and saves it into my real description field:
class WidgetForm(forms.ModelForm):
extra_field = forms.CharField(required=False)
def processData(self, input):
# example of error handling
if False:
raise forms.ValidationError('Processing failed!')
return input + " has been processed"
def save(self, commit=True):
extra_field = self.cleaned_data.get('extra_field', None)
# self.description = "my result" note that this does not work
# Get the form instance so I can write to its fields
instance = super(WidgetForm, self).save(commit=commit)
# this writes the processed data to the description field
instance.description = self.processData(extra_field)
if commit:
instance.save()
return instance
class Meta:
model = Widget
fields = "__all__"
You can always create new admin template, and do what you need in your admin_view (override the admin add URL to your admin_view):
url(r'^admin/mymodel/mymodel/add/$','admin_views.add_my_special_model')
If you absolutely only want to store the combined field on the model and not the two seperate fields, you could do something like this:
Create a custom form using the form attribute on your ModelAdmin. ModelAdmin.form
Parse the custom fields in the save_formset method on your ModelAdmin. ModelAdmin.save_model(request, obj, form, change)
I never done something like this so I'm not completely sure how it will work out.
The first (highest score) solution (https://stackoverflow.com/a/23337009/10843740) was accurate, but I have more.
If you declare fields by code, that solution works perfectly, but what if you want to build those dynamically?
In this case, creating fields in the __init__ function for the ModelForm won't work. You will need to pass a custom metaclass and override the declared_fields in the __new__ function!
Here is a sample:
class YourCustomMetaClass(forms.models.ModelFormMetaclass):
"""
For dynamically creating fields in ModelForm to be shown on the admin panel,
you must override the `declared_fields` property of the metaclass.
"""
def __new__(mcs, name, bases, attrs):
new_class = super(NamedTimingMetaClass, mcs).__new__(
mcs, name, bases, attrs)
# Adding fields dynamically.
new_class.declared_fields.update(...)
return new_class
# don't forget to pass the metaclass
class YourModelForm(forms.ModelForm, metaclass=YourCustomMetaClass):
"""
`metaclass=YourCustomMetaClass` is where the magic happens!
"""
# delcare static fields here
class Meta:
model = YourModel
fields = '__all__'
This is what I did to add the custom form field "extra_field" which is not the part of the model "MyModel" as shown below:
# "admin.py"
from django.contrib import admin
from django import forms
from .models import MyModel
class MyModelForm(forms.ModelForm):
extra_field = forms.CharField()
def save(self, commit=True):
extra_field = self.cleaned_data.get('extra_field', None)
# Do something with extra_field here
return super().save(commit=commit)
#admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
You might get help from my answer at :
my response previous on multicheckchoice custom field
You can also extend multiple forms having different custom fields and then assigning them to your inlines class like stackedinline or tabularinline:
form =
This way you can avoid formset complication where you need to add multiple custom fields from multiple models.
so your modeladmin looks like:
inlines = [form1inline, form2inline,...]
In my previous response to the link here, you will find init and save methods.
init will load when you view the page and save will send it to database.
in these two methods you can do your logic to add strings and then save thereafter view it back in Django admin change_form or change_list depending where you want.
list_display will show your fields on change_list.
Let me know if it helps ...
....
class CohortDetailInline3(admin.StackedInline):
model = CohortDetails
form = DisabilityTypesForm
...
class CohortDetailInline2(admin.StackedInline):
model = CohortDetails
form = StudentRPLForm
...
...
#admin.register(Cohort)
class CohortAdmin(admin.ModelAdmin):
form = CityInlineForm
inlines = [uploadInline, cohortDetailInline1,
CohortDetailInline2, CohortDetailInline3]
list_select_related = True
list_display = ['rto_student_code', 'first_name', 'family_name',]
...