django model instance variables for transient use - python

I would like to create some instance variables for my model subclass, but when saving the object to the database I don't want a table column for that variable. I read in some places you would do this by overriding init() like how you would create normal instance variables in other classes. Is this the accepted way for subclasses of model? Are there other approaches?
models.py:
class MyModel (models.Model):
name = models.CharField(max_length=300)
def __init__(self, *args, **kwargs):
super(MyModel, self).__init__(*args, **kwargs)
self.tempvar = ''
views.py:
myModel = MyModel()
myModel.tempvar = 'this will not be saved in the database'

That's an acceptable way of doing it, although you don't need to initialize it unless there's a chance you may try to access it when it doesn't exist. Also, consider if you should be using a property instead.

Update for Python 3: You can call super() directly without repeating the class name
class MyModel (models.Model):
name = models.CharField(max_length=300)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tempvar = ''

Related

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, ...)

Pass a argument to Django meta class

I can alternatively create different types of form, but that's tedious.
So is it possible to pass the type to the form,then show the form accordingly?
This code shows NameError: name 'review_type' is not defined
class Contest1_for_review(ModelForm, review_type):
class Meta:
model = Contest1
decision = review_type + '_decision'
comment = review_type +'comment'
fields = [
decision,
comment,
]
Is it possible to pass a argument to meta class, like this?
Form is a class and when its rendered in the HTML, its rendering an instance of the form class. So when passing a value to that instance, you can use its __init__ method. For example:
class Contest1_for_review(ModelForm):
def __init__(self, *args, **kwargs):
review_type = kwargs.pop('review_type') # <-- getting the value from keyword arguments
super().__init__(*args, **kwargs)
self.fields[f'{review_type}_decision'] = forms.CharField()
self.fields[f'{review_type}_comment'] = forms.CharField()
class Meta:
model = Contest1
fields = "__all__"
Also, you need to send the value of review_type from view to form. Like this in function based view:
form = Contest1_for_review(review_type="my_value")
Or use get_form_kwargs to send the value from a Class based view. FYI: you don't need to change anything in Meta class.
Update:
From discussion in comments, OP should use forms.Form instead of ModelForm as using model form requires fields /exclude value in Meta class.

Django get and use instance and field name on upload_to

I'm trying to build a path for a FileField, getting and using the instance to get another data for the URL, plus the field name, to get something like:
/media/documents/<instance_data>/<field_name>.pdf
My best working approach is:
class UserDocFileField(models.FileField):
def get_fixed_folder_path(self, instance, filename):
return 'documents/{}/{}.pdf'.format(instance.user.rfc, self.name)
def __init__(self, *args, **kwargs):
kwargs["upload_to"] = self.get_fixed_folder_path
super(UserDocFileField, self).__init__(*args, **kwargs)
And in my model:
class Documents(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
file_1 = UserDocFileField()
file_2 = UserDocFileField()
# ... other documents
Giving me what I'm looking for, i.e.:
/media/documents/ABCD840422ABC/file_1.pdf
However, this makes Django to generate a migration file every single time I run makemigrations, I have tried to set it as an inner class, rewriting the super as
super(Documents.UserDocFileField, self).__init__(*args, **kwargs)
But, I got this error:
NameError: name 'Documents' is not defined
So, is there a way to avoid the generations of migrations files or a better approach to solve this?
One way of doing this is to use a custom class for the upload_to itself, with a __call__ method to make the instance callable. In order to make that serializable for migrations you then need to add a deconstruct method. So:
class UploadTo:
def __init__(self, name):
self.name = name
def __call__(self, instance, filename):
return 'documents/{}/{}.pdf'.format(instance.user.rfc, self.name)
def deconstruct(self):
return ('myapp.models.UploadTo', [self.fieldname], {})
class Documents(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
file_1 = FileField(upload_to=UploadTo('file_1'))
file_2 = FileField(upload_to=UploadTo('file_2'))
Honestly though, at this point I'd probably just write separate upload_to functions for each field.

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.

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

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()

Categories

Resources