31👍
As already implied in @gareth’s answer, hard-coding a default id
value might not always be the best idea:
If the id
value does not exist in the database, you’re in trouble. Even if that specific id
value does exist, the corresponding object may change. In any case, when using a hard-coded id
value, you’d have to resort to things like data-migrations or manual editing of existing database content.
To prevent that, you could use get_or_create() in combination with a unique
field (other than id
).
Here’s one way to do it:
from django.db import models
class Exam(models.Model):
title = models.CharField(max_length=255, unique=True)
description = models.CharField(max_length=255)
@classmethod
def get_default_pk(cls):
exam, created = cls.objects.get_or_create(
title='default exam',
defaults=dict(description='this is not an exam'),
)
return exam.pk
class Student(models.Model):
exam_taken = models.ForeignKey(
to=Exam, on_delete=models.CASCADE, default=Exam.get_default_pk
)
Here an Exam.title
field is used to get a unique object, and an Exam.description
field illustrates how we can use the defaults
argument (for get_or_create
) to fully specify the default Exam
object.
Note that we return a pk
, as suggested by the docs:
For fields like
ForeignKey
that map to model instances, defaults should be the value of the field they reference (pk
unlessto_field
is set) instead of model instances.
Also note that default
callables are evaluated in Model.__init__()
(source). So, if your default value depends on another field of the same model, or on the request context, or on the state of the client-side form, you should probably look elsewhere.
25👍
I would modify @vault’s answer above slightly (this may be a new feature). It is definitely desirable to refer to the field by a natural name. However instead of overriding the Manager
I would simply use the to_field
param of ForeignKey
:
class Country(models.Model):
sigla = models.CharField(max_length=5, unique=True)
def __unicode__(self):
return u'%s' % self.sigla
class City(models.Model):
nome = models.CharField(max_length=64, unique=True)
nation = models.ForeignKey(Country, to_field='sigla', default='IT')
- [Django]-ManyRelatedManager object is not iterable
- [Django]-CORS error while consuming calling REST API with React
- [Django]-What is the best django model field to use to represent a US dollar amount?
14👍
I use natural keys to adopt a more natural approach:
<app>/models.py
from django.db import models
class CountryManager(models.Manager):
"""Enable fixtures using self.sigla instead of `id`"""
def get_by_natural_key(self, sigla):
return self.get(sigla=sigla)
class Country(models.Model):
objects = CountryManager()
sigla = models.CharField(max_length=5, unique=True)
def __unicode__(self):
return u'%s' % self.sigla
class City(models.Model):
nome = models.CharField(max_length=64, unique=True)
nation = models.ForeignKey(Country, default='IT')
- [Django]-Using Django time/date widgets in custom form
- [Django]-How to server HTTP/2 Protocol with django
- [Django]-How to pass django rest framework response to html?
11👍
The issue with most of these approaches are that they use HARD CODED values or lambda methods inside the Model which are not supported anymore since Django Version 1.7.
In my opinion, the best approach here is to use a sentinel method which can also be used for the on_delete
argument.
So, in your case, I would do
# Create or retrieve a placeholder
def get_sentinel_exam():
return Exam.objects.get_or_create(name="deleted",grade="N/A")[0]
# Create an additional method to return only the id - default expects an id and not a Model object
def get_sentinel_exam_id():
return get_sentinel_exam().id
class Exam(models.Model):
....
# Making some madeup values
name=models.CharField(max_length=200) # "English", "Chemistry",...
year=models.CharField(max_length=200) # "2012", "2022",...
class Student(models.Model):
....
.....
exam_taken = models.ForeignKey("Exam",
on_delete=models.SET(get_sentinel_exam),
default=get_sentinel_exam_id
)
Now, when you just added the exam_taken
field uses a guaranteed existing value while also, when deleting the exam, the Student themself are not deleted and have a foreign key to a deleted
value.
- [Django]-The QuerySet value for an exact lookup must be limited to one result using slicing. Filter error
- [Django]-How can I check the size of a collection within a Django template?
- [Django]-Why is logged_out.html not overriding in django registration?
9👍
In my case, I wanted to set the default to any existing instance of the related model. Because it’s possible that the Exam
with id 1
has been deleted, I’ve done the following:
class Student(models.Model):
exam_taken = models.ForeignKey("Exam", blank=True)
def save(self, *args, **kwargs):
try:
self.exam_taken
except:
self.exam_taken = Exam.objects.first()
super().save(*args, **kwargs)
If exam_taken
doesn’t exist, django.db.models.fields.related_descriptors.RelatedObjectDoesNotExist
will be raised when a attempting to access it.
- [Django]-Annotate with latest related object in Django
- [Django]-How to rename items in values() in Django?
- [Django]-Stack trace from manage.py runserver not appearing
4👍
You could use this pattern:
class Other(models.Model):
DEFAULT_PK=1
name=models.CharField(max_length=1024)
class FooModel(models.Model):
other=models.ForeignKey(Other, default=Other.DEFAULT_PK)
Of course you need to be sure that there is a row in the table of Other
. You should use a datamigration to be sure it exists.
- [Django]-Django Rest Framework with ChoiceField
- [Django]-How to delete a record in Django models?
- [Django]-Django "xxxxxx Object" display customization in admin action sidebar
1👍
You need to use get_or_create() in get_exam()
and on_delete for models.ForeignKey() as shown below. *Don’t forget to put .id
just after get_or_create(id=1)[0]
because default in models.ForeignKey()
needs id
of an Exam
object otherwise there is an error and you can see my answer explaining about get_or_create()
:
def get_exam(): # Here # ↓ Don't forget
return Exam.objects.get_or_create(id=1)[0].id
class Student(models.Model):
# ...
exam_taken = models.ForeignKey(
"Exam",
default=get_exam,
on_delete=models.CASCADE # Here
)
And, you can put get_exam()
in Student
class as shown below:
class Student(models.Model):
# ...
def get_exam(): # Here
return Exam.objects.get_or_create(id=1)[0].id
exam_taken = models.ForeignKey(
"Exam",
default=get_exam,
on_delete=models.CASCADE
)
I recommend to use @classmethod
for get_exam()
in Student
class as shown below so that other classes can also use get_exam()
by class name. *@classmethod
can do more things than @staticmethod
according to my answer:
class Student(models.Model):
# ...
@classmethod # Here
def get_exam(cls):
return Exam.objects.get_or_create(id=1)[0].id
exam_taken = models.ForeignKey(
"Exam",
default=get_exam,
on_delete=models.CASCADE
)
And instead of get_exam
, you can assign get_exam()
to default
as shown below:
def get_exam():
return Exam.objects.get_or_create(id=1)[0].id
class Student(models.Model):
# ...
exam_taken = models.ForeignKey(
"Exam", # ↓ Here ↓
default=get_exam(),
on_delete=models.CASCADE
)
class Student(models.Model):
# ...
def get_exam():
return Exam.objects.get_or_create(id=1)[0].id
exam_taken = models.ForeignKey(
"Exam", # ↓ Here ↓
default=get_exam(),
on_delete=models.CASCADE
)
- [Django]-Django import error – No module named core.management
- [Django]-Problems with contenttypes when loading a fixture in Django
- [Django]-Setting DEBUG = False causes 500 Error
0👍
I’m looking for the solution in Django Admin, then I found this:
class YourAdmin(admin.ModelAdmin)
def get_changeform_initial_data(self, request):
return {'owner': request.user}
this also allows me to use the current user.
- [Django]-How to manage local vs production settings in Django?
- [Django]-How to add multiple objects to ManyToMany relationship at once in Django ?
- [Django]-The QuerySet value for an exact lookup must be limited to one result using slicing. Filter error
0👍
the best way I know is to use lambdas
class TblSearchCase(models.Model):
weights = models.ForeignKey('TblSearchWeights', models.DO_NOTHING, default=lambda: TblSearchWeights.objects.get(weight_name='value_you_want'))
so you can specify the default row..
default=lambda: TblSearchWeights.objects.get(weight_name='value_you_want')
- [Django]-Celery : Execute task after a specific time gap
- [Django]-How to override css in Django Admin?
- [Django]-How to solve "Page not found (404)" error in Django?
0👍
I’m adding something on top of @djvg answer and providing some illustration to @Super Kai one’s concerning the class method way of solving this problem:
Using a class method is also useful in this case, and probably in a lot of other cases, because you may have to add other ForeignKey relationships to your model later on like so:
from django.db import models
class Exam(models.Model):
title = models.CharField(max_length=255, unique=True)
description = models.CharField(max_length=255)
@classmethod
def get_default_pk(cls):
exam, created = cls.objects.get_or_create(
title='default exam',
defaults=dict(description='this is not an exam'),
)
return exam.pk
class Student(models.Model):
exam_taken = models.ForeignKey(
to=Exam, on_delete=models.CASCADE, default=Exam.get_default_pk
)
# NEW
class Teacher(models.Model):
exam_given = models.ForeignKey(
to=Exam, on_delete=models.CASCADE, default=Exam.get_default_pk
)
In my case, I came here to figure out how to provide a default image to a Photo model whom I wanted to be used by several other models that would also use that default image like so:
class Photo(models.Model):
title = models.CharField(max_length=200, unique=True)
image = models.ImageField(upload_to='photos',
default="default_photo_folder/default_image")
@classmethod
def get_or_create_default_photo_pk(cls):
obj, created_bool = cls.objects.get_or_create(
title='default photo', # ↓ title value will be automatically included in defaults ↓
defaults=dict(image="default_photo_folder/default_image"),
)
return obj.pk
------------------------------------------------------------------
# From photoapp.models import Photo
class Event(models.Model):
photo = models.ForeignKey(Photo,
on_delete=models.SET_DEFAULT,
related_name='event_photos',
default=Photo.get_or_create_default_photo_pk)
class Place(models.Model):
photo = models.ForeignKey(Photo,
on_delete=models.SET_DEFAULT,
related_name='place_photos',
default=Photo.get_or_create_default_photo_pk)
Please note that in this example I already have a default photo in my media/default_photo folder. I also used models.SET_DEFAULT to set my default photo as the object’s photo when its original photo would be deleted. As stated in the doc, I also used a unique field lookup in my get_or_create() method to avoid extra headache .
- [Django]-Parsing unicode input using python json.loads
- [Django]-Django model blank=False does not work?
- [Django]-Getting TypeError: __init__() missing 1 required positional argument: 'on_delete' when trying to add parent table after child table with entries