I just researched my "bug" and it turned out to be a new feature in Django 1.9 that CharFields strip spaces by default :
https://docs.djangoproject.com/en/1.9/ref/forms/fields/#django.forms.CharField.strip
The same seams to apply to text fields TextField.
So I found out why Django suddenly behaves differently than before, but is there an easy way to restore the previous default for auto generated admin forms?
I would like to NOT strip spaces while still using the auto generated form from the admin. Is that still possible?
If you are looking for a text/char field and do not want it to strip white spaces you can set strip=False in the constructor method of a form and then use the form in the admin
class YourForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(YourForm, self).__init__(*args, **kwargs)
self.fields['myfield'].strip = False
class Meta:
model = YourModel
fields = "__all__"
You can then use this form in the admin by specifying form=YourForm in the admin.py file.
Try using this:
# fields.py
from django.db.models import TextField
class NonStrippingTextField(TextField):
"""A TextField that does not strip whitespace at the beginning/end of
it's value. Might be important for markup/code."""
def formfield(self, **kwargs):
kwargs['strip'] = False
return super(NonStrippingTextField, self).formfield(**kwargs)
And in your model:
class MyModel(models.Model):
# ...
my_field = NonStrippingTextField()
strip=False
in the model field for CharFields.
Django TextField do not support this stripping feature so you have to do it on your own. You can use the strip method.
abc.strip()
Seems like the best way to handle this is to create a custom admin form like this:
class CustomForm(forms.ModelForm):
my_field = forms.CharField(strip=False, widget=forms.Textarea)
class Meta:
model = MyModel
exclude = []
This will create a default form with just my_field overwritten with its non stripped version. )this has to be set in the corresponding admin of course. If anybody knows an even simpler version. Please tell me!
You can also achieve this by modifying ModelAdmin.
Overridding formfield_for_dbfield function and setting kwargs['strip'] = False for title field will disable auto trim for it.
#admin.register(Example)
class ExampleAdmin(admin.ModelAdmin):
def formfield_for_dbfield(self, db_field, request, **kwargs):
if db_field.name == 'title':
kwargs['strip'] = False
return super().formfield_for_dbfield(db_field, request, **kwargs)
Ref: https://www.aaronoellis.com/articles/allow-whitespace-to-be-a-valid-charfield-value-in-django-admin
I was having this issue with django-rest model serializer. The data in my text field was stripped of white space. So if you are trying to do this on the serializer level, you can specify the whitespace param on CharField serializer. Here is the source code signature.
And here is the rest-docs on CharField
class SomeSerializer(serializers.ModelSerializer):
content = serializers.CharField(trim_whitespace=False)
class Meta:
model = YourModel
fields = ["content"]
Related
I want to overwrite the __str__ method in Django admin when using the autocomplete_fields = () but the returned values are using __str__.
I have a form something like
class MyAdminForm(forms.ModelForm):
placement = forms.Select(
choices = Organisation.objects.active(),
)
class Meta:
model = Lead
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['placement'].label_from_instance = lambda obj: f'{str(obj)} {obj.post_code}'
This will provide back a Select with the organisation name and post code in the dropdown fields. But there are some 80k choices so I need to using autocomplete. Within within admin.py I have
class LeadAdmin(admin.ModelAdmin):
form = LeadAdminForm
autocomplete_fields = ('placement',)
As soon as I add the autocomplete_fields I lose my postcode and it reverts to just showing the __str__
Hoa can I used autocomplete_fields and overwrite the __str__ method?
This question is answered through Benbb96 comment above which I've copied here so I can close it
So maybe this answer can help you :
stackoverflow.com/a/56865950/8439435 – Benbb96
I am adding an extra field to a Django ModelForm like that:
class form(forms.ModelForm):
extra_field = forms.CharField(label='Name of Institution')
class Meta:
model = db_institutionInstitution
fields = ['conn_kind','time','inst_name2']
The form is actually working fine, but I cant prepopulate it. I use it in a modelformset_factory:
formset = modelformset_factory(db_institutionInstitution,form=form)
I manually run through a queryset and add the entry in the dictionary needed for the additional form in the formset. However, when I call:
formset1 = formset(prefix='brch',queryset=qs1)
the extra_field is not prepopulated as intended (the rest is working fine).
Can anyone help?
If you want to set a default.
extra_field = forms.CharField(label='Name of Institution', initial="harvard")
If you want to dynamically set a value put it on form initialization:
def __init__(self, *args, **kwargs):
super(form, self).__init__(*args, **kwargs)
self.fields['extra_field'].initial = "harvard"
I'm overriding the admin form of a model to modify the choices of a ForeignKey field.
When selecting a choice in the admin form, and saving, I get a ValueError:
Cannot assign "u'6'": "MyModel1.mymodel2" must be a "MyModel2" instance
where 6 is the id of the selected choice.
The new choices is built as ((<choice_1_id>, <choice_1_label>), (<choice_2_id>, <choice_2_label>),...), and I get the same html for the rendered select widget as if I don't modify the choices (apart from the ordering of course).
If I comment self.fields['mymodel2'] = forms.ChoiceField(choices=choices) in MyModel1AdminForm.__init__() I get no error...
Anybody could help?
models.py
class MyModel1(models.Model):
mymodel2 = ForeignKey(MyModel2)
# more fields...
admin.py
class MyModel1AdminForm(forms.ModelForm):
class Meta:
model = MyModel1
def __init__(self, *args, **kwargs):
super(MyModel1AdminForm, self).__init__(*args, **kwargs)
# create choices with ((<choice_1_id>, <choice_1_label>), (<choice_2_id>, <choice_2_label>),...)
self.fields['mymodel2'] = forms.ChoiceField(choices=choices, widget=SelectWithDisabled) # http://djangosnippets.org/snippets/2453/
class MyModel1Admin(admin.ModelAdmin):
form = MyModel1AdminForm
my_site.register(MyModel1, MyModel1Admin)
mymodel2 is the Foreign Key field. You need to supply the queryset if you want to change the choices instead of adding your custom choices:
self.fields['mymodel2'].queryset = MyModel2.objects.all()
If you need to construct the choices manually something like
choices = MyModel2.objects.values_list('pk', 'title')
should work with a standard ChoiceField, where title is the field of the model you want to use as label/verbose name for your choice.
Looking at the snippet you are using values_list won't work so you could fallback to a list comprehension:
[(c.pk, {'label': c.title, 'disabled': False}) for c in MyModel2.objects.all()]
Though you obviously need some more logic to decide whether a choice is enabled or disabled.
I ended up overriding ModelChoiceField:
class MyModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
level = getattr(obj, obj._mptt_meta.level_attr)
return {'label': obj.name), 'disabled': check_disabled(obj)}
I'm using django modelform inheritence in my modelform but it seems to be not working here is my code sample
class ArticleForm(forms.ModelForm):
title = forms.CharField(required=True)
sites = forms.ModelMultipleChoiceField(required=True, queryset= Sites.objects.all().order_by('name'), widget=forms.SelectMultiple())
class ArticleAddForm(ArticleForm):
class Meta(ArticleForm.Meta):
exclude = ('sites',)
i want to exclude "sites" from "ArticleAddForm" but while validating it is raising form validation error sites field required please help?
ModelForms don't handle inheritance so well, I believe.
Probably the best ou can do is remove the required flag in the child class:
def __init__(self, *args, **kwargs):
super(ArticleAddForm, self).__init__(*args, **kwargs)
self.base_fields['sites'].required = False
self.base_fields['sites'].widget = HiddenInput() # if you want
In your view, you need to initialize the ArticleAddForm with an Article object to fill the blank fields, i.e. the excluded fields. For example:
sites = Sites.objects.all() # modify this according to your needs
article = Article(title='', sites=sites)
form = ArticleAddForm(request.POST, instance=article)
form.save()
Even though a field is marked as 'editable=False' in the model, I would like the admin page to display it. Currently it hides the field altogether.. How can this be achieved ?
Use Readonly Fields. Like so (for django >= 1.2):
class MyModelAdmin(admin.ModelAdmin):
readonly_fields=('first',)
Update
This solution is useful if you want to keep the field editable in Admin but non-editable everywhere else. If you want to keep the field non-editable throughout then #Till Backhaus' answer is the better option.
Original Answer
One way to do this would be to use a custom ModelForm in admin. This form can override the required field to make it editable. Thereby you retain editable=False everywhere else but Admin. For e.g. (tested with Django 1.2.3)
# models.py
class FooModel(models.Model):
first = models.CharField(max_length = 255, editable = False)
second = models.CharField(max_length = 255)
def __unicode__(self):
return "{0} {1}".format(self.first, self.second)
# admin.py
class CustomFooForm(forms.ModelForm):
first = forms.CharField()
class Meta:
model = FooModel
fields = ('second',)
class FooAdmin(admin.ModelAdmin):
form = CustomFooForm
admin.site.register(FooModel, FooAdmin)
Add the fields you want to display on your admin page.
Then add the fields you want to be read-only.
Your read-only fields must be in fields as well.
class MyModelAdmin(admin.ModelAdmin):
fields = ['title', 'author', 'published_date', 'updated_date', 'created_date']
readonly_fields = ('updated_date', 'created_date')
You could also set the readonly fields as editable=False in the model (django doc reference for editable here). And then in the Admin overriding the get_readonly_fields method.
# models.py
class MyModel(models.Model):
first = models.CharField(max_length=255, editable=False)
# admin.py
class MyModelAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
return [f.name for f in obj._meta.fields if not f.editable]
With the above solution I was able to display hidden fields for several objects but got an exception when trying to add a new object.
So I enhanced it like follows:
class HiddenFieldsAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
try:
return [f.name for f in obj._meta.fields if not f.editable]
except:
# if a new object is to be created the try clause will fail due to missing _meta.fields
return ""
And in the corresponding admin.py file I just had to import the new class and add it whenever registering a new model class
from django.contrib import admin
from .models import Example, HiddenFieldsAdmin
admin.site.register(Example, HiddenFieldsAdmin)
Now I can use it on every class with non-editable fields and so far I saw no unwanted side effects.
You can try this
#admin.register(AgentLinks)
class AgentLinksAdmin(admin.ModelAdmin):
readonly_fields = ('link', )