Changing case (upper/lower) on adding data through Django admin site - python

I'm configuring the admin site of my new project, and I have a little doubt on how should I do for, on hitting 'Save' when adding data through the admin site, everything is converted to upper case...
Edit: Ok I know the .upper property, and I I did a view, I would know how to do it, but I'm wondering if there is any property available for the field configuration on the admin site :P

If your goal is to only have things converted to upper case when saving in the admin section, you'll want to create a form with custom validation to make the case change:
class MyArticleAdminForm(forms.ModelForm):
class Meta:
model = Article
def clean_name(self):
return self.cleaned_data["name"].upper()
If your goal is to always have the value in uppercase, then you should override save in the model field:
class Blog(models.Model):
name = models.CharField(max_length=100)
def save(self, force_insert=False, force_update=False):
self.name = self.name.upper()
super(Blog, self).save(force_insert, force_update)

Updated example from documentation suggests using args, kwargs to pass through as:
Django will, from time to time, extend the capabilities of built-in
model methods, adding new arguments. If you use *args, **kwargs in
your method definitions, you are guaranteed that your code will
automatically support those arguments when they are added.
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super(Blog, self).save( *args, **kwargs) # Call the "real" save() method.
do_something_else()

you have to override save(). An example from the documentation:
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, force_insert=False, force_update=False):
do_something()
super(Blog, self).save(force_insert, force_update) # Call the "real" save() method.
do_something_else()

Related

Unable to update/delete related model in the save() method of a model

I have two Django models:
from django.db import models
class Policy(models.Model):
status = models.IntegerField()
def save(self, *args, **kwargs):
quote = self.documents.get(document_type=DocumentType.quote)
if self.status == 0:
quote.delete()
elif self.status == 1:
new_quote_content = create_new_quote()
quote.s3_file.save(quote.name, File(new_quote_content))
super().save(*args, *kwargs)
class Document(models.Model):
policy = models.ForeignKey(
to=Policy,
null=True,
blank=True,
on_delete=models.CASCADE,
related_name="documents",
)
s3_file = models.FileField(
storage=S3Storage(aws_s3_bucket_name="policy-documents"),
upload_to=get_document_s3_key,
max_length=255,
)
I want to delete/update the document when the policy status is updated and I've overriden the save() method in Policy to do it. However, neither the doc deletion nor the doc's FieldFile update works in the save() method. If I move them to outside the save() method, everything works.
Does someone understand what's the issue here?
It is not calling the super method to save the model. To override a model it has to be something like this as given in the documentation of Django.
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super().save(*args, **kwargs) # Call the "real" save() method.
do_something_else()
The save() was being called from a Policy ModelAdmin with a inlined Document form set. After it was run, Django executed the ModelAdmin's save_related() method, which saved the Document form set data, overwriting the Document changes I had just saved in save(). I solved it by overriding save_related() and deleting/updating the document after form.save_m2m() and form.save_formset().

Django: modify model’s field before save

I have a model, course, with an ImageField and FileField so I’d like to create a folder each time the user create a course. I think I can do this before the model is saved so here is my question.
How can I access a model’s fields in a method?
Models.py
Class Course(models.Model):
Thumbnail = model.ImageField(upload_to=“...”,...)
def save(self, *args, **kwargd):
... #How can I alter here the ImageField parameters?
super().save(*args, **kwargs)
See here on getting the model's fields.
To get field of an object instance then it should just be as
def save(self, *args, **kwargs):
#### How can I alter here the ImageField parameters?
self.Thumbnail = #do whatever you want here
super().save(*args, **kwargs)
It's not possible to alter field's params in a method. It must only be done in the field definitions because the model must be saved first
Instance of the models field can be accessed using self like a normal instance variable.
For example in a model class below,
class DummyModel(models.Model):
key = models.CharField()
value = models.TextField()
def save(self, *args, **kwargs):
value = self.value # self.value is a model field.
super().save(*args, **kwargs)
In your case, you can access it using self.Thumbnail
Since it's not possible to modify the model field's parameters before create it, as #azyCrw4282 said in his answer, it's possible to create a directory with the name of the model's instance and upload there the files parsing a function to upload_to
models.py
def upload_to(instance, filename):
return 'user_course/{0}/{1}'.format(instance.name, filename) #instance.name will be the name of the course
class Course(models.Model):
name = model.CharField(...)
thumbnail = models.ImageField(upload_to=upload_to, ...)

Django limit_choices_to on ManyToMany related with Inline

I would like to use limit_choices_to to reduce the set of choices for the Django admin of Model with a ManyToMany Field when using an Inline.
Interestingly, the reason why I want to limit choices is for performance as I want to use a property of a parent model in the __str__ method of my related model class, and not limiting the choices causes prohibitively many SQL queries.
The following works
class ParentOfA(models.Model):
name = models.CharField(max_length=50, null=True)
class A(models.Model):
parent = models.ForeignKey(ParentOfA)
def __str__(self):
return "%s" % self.parent
class B(models.Model):
a = models.ManyToManyField(A, limit_choices_to={"a__name":'parent name'})
If I don't use an Inline in the admin form for B (following the example in the docs).
E.g.
#admin.register(B)
class BAdmin(admin.ModelAdmin):
pass
However, with the inline the limit_choices_to has no effect:
class BInline(admin.TabularInline):
model = B.A.through
#admin.register(B)
class BAdmin(admin.ModelAdmin):
inline = (BInline,)
Any suggestions?
I don't know why limit_choices_to doesn't work for the inline field, I am having the same problem. While this should not be necessary, you can limit the queryset of a field in an inline using this answer: https://stackoverflow.com/a/4236159/1302095
This is an old answer to an old question, so I'm not sure if there are better ways in newer versions of django. I am using 1.8.3 and it works, which is what matters to me!
Code pasted here for reference:
class RoomInline(admin.TabularInline):
model = Room
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
field = super(RoomInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
if db_field.name == 'inside_room':
if request._obj_ is not None:
field.queryset = field.queryset.filter(building__exact = request._obj_)
else:
field.queryset = field.queryset.none()
return field
class BuildingAdmin(admin.ModelAdmin):
inlines = (RoomInline,)
def get_form(self, request, obj=None, **kwargs):
# just save obj reference for future processing in Inline
request._obj_ = obj
return super(BuildingAdmin, self).get_form(request, obj, **kwargs)

How to tell if a model instance is new or not when using UUIDField as a Primary Key

I have a model that requires some post-processing (I generate an MD5 of the body field).
models.py
class MyModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
body = models.TextField()
md5 = models.CharField(max_length=32)
...
def save(self, *args, **kwargs):
if self.pk is None: # Only for create (edit disabled)
self.md5 = get_md5(self.body)
super(MyModel, self).save(*args, **kwargs)
The problem is that the final block won't execute because I don't see a way to check if the instance is new or not: self.pk is never None because a UUID is populated before saving.
I'd like to know what the best practice is for handling this.
Thanks in advance.
Update:
The only solution I can think of is to call the database directly and:
Check if the id exists
Compare the modified and created fields to tell if it's an edit
EDIT
self.pk is never None because a UUID is populated before saving.
Instead of setting a default for id, use a method to set id for the new instance.
class MyModel(...):
id = models.UUIDField(primary_key=True, default=None,...)
def set_pk(self):
self.pk = uuid.uuid4()
def save(self, *args, **kwargs):
if self.pk is None:
self.set_pk()
self.md5 = get_md5(self.body)
super(MyModel, self).save(*args, **kwargs)
Looks like the cleanest approach to this is to make sure that all your models have a created date on them by inheriting from an Abstract model, then you simply check if created has a value:
models.py
class BaseModel(models.Model):
"""
Base model which all other models can inherit from.
"""
id = fields.CustomUUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
# Abstract models are not created in the DB
abstract = True
class MyModel(BaseModel):
my_field = models.CharField(max_length=50)
def save(self, *args, **kwargs):
if self.created:
# Do stuff
pass
super(MyModel, self).save(*args, **kwargs)
I just got the same issue in my project and found out that you can check the internal state of the model's instance:
def save(self, *args, **kwargs):
if self._state.adding: # Only for create (edit disabled)
self.md5 = get_md5(self.body)
super(MyModel, self).save(*args, **kwargs)
But this solution relies on internal implementation and may stop working after Django is updated
As I've answered here as well, the cleanest solution I've found that doesn't require any additional datetime fields or similar tinkering is to plug into the Django's post_save signal. Add this to your models.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=MyModel)
def mymodel_saved(sender, instance, created, **kwargs):
if created:
# do extra work on your instance
self.md5 = get_md5(self.body)
This callback will block the save method, so you can do things like trigger notifications or update the model further before your response is sent back over the wire, whether you're using forms or the Django REST framework for AJAX calls. Of course, use responsibly and offload heavy tasks to a job queue instead of keeping your users waiting :)

How to set True as default value for BooleanField on Django?

I am using BooleanField in Django.
By default, the checkbox generated by it is unchecked state. I want the state to be checked by default. How do I do that?
If you're just using a vanilla form (not a ModelForm), you can set a Field initial value ( https://docs.djangoproject.com/en/2.2/ref/forms/fields/#django.forms.Field.initial ) like
class MyForm(forms.Form):
my_field = forms.BooleanField(initial=True)
If you're using a ModelForm, you can set a default value on the model field ( https://docs.djangoproject.com/en/2.2/ref/models/fields/#default ), which will apply to the resulting ModelForm, like
class MyModel(models.Model):
my_field = models.BooleanField(default=True)
Finally, if you want to dynamically choose at runtime whether or not your field will be selected by default, you can use the initial parameter to the form when you initialize it:
form = MyForm(initial={'my_field':True})
from django.db import models
class Foo(models.Model):
any_field = models.BooleanField(default=True)
I am using django==1.11. The answer get the most vote is actually wrong. Checking the document from django, it says:
initial -- A value to use in this Field's initial display. This value
is not used as a fallback if data isn't given.
And if you dig into the code of form validation process, you will find that, for each fields, form will call it's widget's value_from_datadict to get actual value, so this is the place where we can inject default value.
To do this for BooleanField, we can inherit from CheckboxInput, override default value_from_datadict and init function.
class CheckboxInput(forms.CheckboxInput):
def __init__(self, default=False, *args, **kwargs):
super(CheckboxInput, self).__init__(*args, **kwargs)
self.default = default
def value_from_datadict(self, data, files, name):
if name not in data:
return self.default
return super(CheckboxInput, self).value_from_datadict(data, files, name)
Then use this widget when creating BooleanField.
class ExampleForm(forms.Form):
bool_field = forms.BooleanField(widget=CheckboxInput(default=True), required=False)
In Django 3.0 the default value of a BooleanField in model.py is set like this:
class model_name(models.Model):
example_name = models.BooleanField(default=False)
I found the cleanest way of doing it is this.
Tested on Django 3.1.5
class MyForm(forms.Form):
my_boolean = forms.BooleanField(required=False, initial=True)
I found the answer here
Another way to check the default state in BooleanField is:
active = forms.BooleanField(
widget=forms.CheckboxInput(
attrs={
'checked': True
}
)
)
Both initial and default properties were not working for me, if that's your case try this:
class MyForm(forms.ModelForm):
validated = forms.BooleanField()
class Meta:
model = MyModel
fields = '__all__'
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['validated'].widget.attrs['checked'] = True
I tried to change inital of BooleanField:
def __init__(self, *args, **kwargs):
super(UserConfirmForm,self).__init__(*args, **kwargs)
self.fields['bool_field'].initial = True
but it didn't work.
My solve:
def __init__(self, *args, **kwargs):
kwargs['initial'] = {'bool_field': True}
super(UserConfirmForm,self).__init__(*args, **kwargs)
It works like:
UserConfirmForm(initial={'bool_field':True})
but we can't call form in Generic editing views.
I think this is a great alternative to a regular call form object.

Categories

Resources