34

How can I set a default value on a ForeignKey field in a django Model or AdminModel?

Something like this (but of course this doesn't work)...

created_by = models.ForeignKey(User, default=request.user)

I know I can 'trick' it in the view, but in terms of the AdminModel it doesn't seem possible.

Max Malysh
  • 29,384
  • 19
  • 111
  • 115
T. Stone
  • 19,209
  • 15
  • 69
  • 97
  • 1
    Did Django have an `AdminModel` in 2009? I don't think it does [today](https://github.com/django/django/search?q=adminmodel&unscoped_q=adminmodel). Did you mean `ModelAdmin`? – djvg Jul 02 '20 at 10:18

14 Answers14

38
class Foo(models.Model):
    a = models.CharField(max_length=42)

class Bar(models.Model):
    b = models.CharField(max_length=42)
    a = models.ForeignKey(Foo, default=lambda: Foo.objects.get(id=1) )
Daniel Magnusson
  • 9,541
  • 2
  • 38
  • 43
  • Are you the same guy of this article? If not please consider adding the link. http://djangodays.com/2009/05/11/django-foreign-key-default-value-example/ – Paolo Jan 29 '13 at 16:48
  • Using symbol names in models for default values is pretty common usage. I do not recall the site, but changed snippet to a shorter pattern. – Daniel Magnusson Feb 03 '13 at 02:34
  • 46
    As of Django 1.7, if you're using Django's migrations, this solution will no longer work as Django cannot serialize lambdas. https://docs.djangoproject.com/en/1.7/topics/migrations/#serializing-values – Eric Ressler Mar 01 '15 at 17:46
  • 8
    @EricRessler How would one make it work in Django 1.10? – MadPhysicist Dec 04 '16 at 21:41
  • 2
    @MadPhysicist This approach works in more modern versions of Django: https://stackoverflow.com/a/7081062/996114 – Mike Covington Dec 05 '17 at 19:14
  • Why id=1? What if you need a dynamic id based on the foreign key? I thought that was the whole point?? – geoidesic Sep 25 '18 at 13:00
17

For django 1.7 or greater,

Just create an ForeignKey object and save it. "default" value can be the id of the the object that should be linked by default.

For example,

created_by = models.ForeignKey(User, default=1)
Rahul Reddy Vemireddy
  • 1,149
  • 1
  • 10
  • 14
11

Here's a solution that will work in Django 1.7. Instead of providing the default at the field definition, set it as null-able, but overide the 'save' function to fill it on the first time (while it's null):

class Foo(models.Model):
    a = models.CharField(max_length=42)

class Bar(models.Model):
    b = models.CharField(max_length=42)
    a = models.ForeignKey(Foo, null=True)

    def save(self, *args, **kwargs):
        if self.a is None:  # Set default reference
            self.a = Foo.objects.get(id=1)
        super(Bar, self).save(*args, **kwargs)
o_c
  • 3,965
  • 1
  • 22
  • 22
  • Do you really need to make the field nullable? I think, that the object is not saved yet, ie. it has not id yet, it isn't "cleaned" unless you put "full_clean" before the super(..save.. So I would change the condition to if self.id is None: and it should work without null=True. – Milano Feb 24 '17 at 14:49
  • @MilanoSlesarik you might be right. The reason I put it there in the first place, is because it was a new field that I added to an existing model (with existing record rows), and so I had to allow it to be null. – o_c Feb 26 '17 at 12:27
  • @o_c Not a good idea as save(..) is not called from batch update QuerySets.. unless you explicitly loop over each returned item and call save, which then defeats the purpose of using a batch update in the first place (and developers tend to forget). – strangetimes May 23 '17 at 11:42
7

I've done this similarly to @o_c, but I'd rather use get_or_create than just plain pk.

class UserSettings(models.Model):
    name = models.CharField(max_length=64, unique=True)
    # ... some other fields 

    @staticmethod
    def get_default_user_settings():
        user_settings, created = UserSettings.objects.get_or_create(
            name = 'Default settings'
        )
        return user_settings

class SiteUser(...):
    # ... some other fields

    user_settings = models.ForeignKey(
        to=UserSettings, 
        on_delete=models.SET_NULL, 
        null=True
    )

    def save(self, *args, **kwargs):
        if self.user_settings is None:
            self.user_settings = UserSettings.get_default_params()
        super(SiteUser, self).save(*args, **kwargs)
Max Malysh
  • 29,384
  • 19
  • 111
  • 115
6

If you are using the development version of Django, you can implement the formfield_for_foreignkey() method on your AdminModel to set a default value.

Steef
  • 33,059
  • 4
  • 45
  • 36
2

As for me, for Django 1.7 its work, just pk:

category = models.ForeignKey(Category, editable=False, default=1)

but remember, that migration looks like

migrations.AlterField(
            model_name='item',
            name='category',
            field=models.ForeignKey(default=1, editable=False, to='item.Category'),
            preserve_default=True,
        ),

so, i don't think it's to be working with dynamic user pk.

LennyLip
  • 1,683
  • 19
  • 19
1
def get_user_default_id():
    return 1

created_by = models.ForeignKey(User, default=get_user_default_id)
Philippos
  • 103
  • 1
  • 8
1

This is a slight modification to the answer from o_c. It should save you one hit on the database. Notice in the save method I use self.a_id = 1 instead of self.a = Foo.objects.get(id=1). It has the same effect without having to query Foo.

class Foo(models.Model):
    a = models.CharField(max_length=42)

class Bar(models.Model):
    b = models.CharField(max_length=42)
    a = models.ForeignKey(Foo, null=True)

    def save(self, *args, **kwargs):
        if self.a is None:  # Set default reference
            self.a_id = 1
        super(Bar, self).save(*args, **kwargs)
Community
  • 1
  • 1
1

For Django >= 1.7, you can set the default for a ForeignKey field in two different ways:

1) Directly as an integer

DEFAULT_CREATED_BY_USER = 42

class MyModel(Model):
    created_by = models.ForeignKey(User, default=DEFAULT_CREATED_BY_USER)

2) As a nonlambda callable returning an integer

You can also use a callable that can be resolved to a full module path. This means that lambdas does not work. But this will:

def default_created_by_user():
    return 42

class MyModel(Model):
    created_by = models.ForeignKey(User, default=default_created_by_user)

References:

Rune Kaagaard
  • 6,643
  • 2
  • 38
  • 29
0

Instead of lambda function use partial function. Lambda function is not serialization and hence gives error in migrations.

class Foo(models.Model):
    a = models.CharField(max_length=42)

class Bar(models.Model):
    b = models.CharField(max_length=42)
    a = models.ForeignKey(Foo, default=partial(Foo.objects.get, id=1 ))
0

You should have settings related to setting.AUTH_USER_MODEL in settings.py file.

You can look in below document:

"https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#substituting-a-custom-user-model"

vikasvmads
  • 542
  • 1
  • 7
  • 12
0

You can override the get_form method of your model admin.

admin.py

class YourModelAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        form = super(YourModelAdmin, self).get_form(request, obj, **kwargs)
        form.base_fields['created_by'].initial = request.user
    return form

admin.site.register(YourModel, YourModelAdmin)
aprasanth
  • 1,079
  • 7
  • 20
0

Out of all along iterations to find a better solution, I find creating a small function will resolve the complexity of task substantially. Because some of the solutions are outdated or longer supported in newer Django versions.

def get_request_user():
    # or any complex query result to set default value in ForeignKey
    return request.user.id

Then you can pass above function as an argument in ForeignKey.

created_by = models.ForeignKey(User, default=get_request_user)
nishit chittora
  • 974
  • 13
  • 20
-2
class table1(models.Model):
    id = models.AutoField(primary_key=True)
    agentname = models.CharField(max_length=20)

class table1(models.Model):
    id = models.AutoField(primary_key=True)
    lastname = models.CharField(max_length=20)
    table1 = models.ForeignKey(Property)