标签归档:django-models

Django auto_now和auto_now_add

问题:Django auto_now和auto_now_add

对于Django 1.1。

我的models.py中有这个:

class User(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

更新行时,我得到:

[Sun Nov 15 02:18:12 2009] [error] /home/ptarjan/projects/twitter-meme/django/db/backends/mysql/base.py:84: Warning: Column 'created' cannot be null
[Sun Nov 15 02:18:12 2009] [error]   return self.cursor.execute(query, args)

我数据库的相关部分是:

  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,

这值得关注吗?

附带问题:在我的管理工具中,这两个字段没有显示。那是预期的吗?

For Django 1.1.

I have this in my models.py:

class User(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

When updating a row I get:

[Sun Nov 15 02:18:12 2009] [error] /home/ptarjan/projects/twitter-meme/django/db/backends/mysql/base.py:84: Warning: Column 'created' cannot be null
[Sun Nov 15 02:18:12 2009] [error]   return self.cursor.execute(query, args)

The relevant part of my database is:

  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,

Is this cause for concern?

Side question: in my admin tool, those two fields aren’t showing up. Is that expected?


回答 0

auto_now设置了属性的任何字段也会继承editable=False,因此不会显示在管理面板中。过去有过关于使auto_nowand auto_now_add参数消失的讨论,尽管它们仍然存在,但我觉得您最好只使用自定义save()方法

因此,为了使其正常工作,我建议不要使用auto_nowauto_now_add而是定义自己的save()方法以确保created仅在id未设置的情况下(例如,首次创建该项目时)对其进行更新,并使其在modified每次该项目更新时进行更新已保存。

我已经使用Django编写的其他项目完成了完全相同的操作,因此您save()将看起来像这样:

from django.utils import timezone

class User(models.Model):
    created     = models.DateTimeField(editable=False)
    modified    = models.DateTimeField()

    def save(self, *args, **kwargs):
        ''' On save, update timestamps '''
        if not self.id:
            self.created = timezone.now()
        self.modified = timezone.now()
        return super(User, self).save(*args, **kwargs)

希望这可以帮助!

编辑以回应评论:

我坚持重载save()与依赖这些字段参数的原因有两个:

  1. 前述的起伏具有其可靠性。这些参数在很大程度上取决于Django知道如何与之交互的每种类型的数据库对待日期/时间戳字段的方式,并且似乎在每个发行版之间都会中断和/或更改。(我相信这是彻底删除它们的呼吁的推动力)。
  2. 它们仅在DateField,DateTimeField和TimeField上起作用,使用这种技术,您可以在每次保存项目时自动填充任何字段类型。
  3. 使用django.utils.timezone.now()vs. datetime.datetime.now(),因为它会根据来返回可感知TZ或天真的datetime.datetime对象settings.USE_TZ

为了解决OP为何看到该错误的原因,我不完全知道,但created尽管有,但看起来根本没有被填充auto_now_add=True。对我来说,它是一个bug,并且在我上面的小列表中强调了项目#1: auto_now并且auto_now_add充其量是片状的。

Any field with the auto_now attribute set will also inherit editable=False and therefore will not show up in the admin panel. There has been talk in the past about making the auto_now and auto_now_add arguments go away, and although they still exist, I feel you’re better off just using a custom save() method.

So, to make this work properly, I would recommend not using auto_now or auto_now_add and instead define your own save() method to make sure that created is only updated if id is not set (such as when the item is first created), and have it update modified every time the item is saved.

I have done the exact same thing with other projects I have written using Django, and so your save() would look like this:

from django.utils import timezone

class User(models.Model):
    created     = models.DateTimeField(editable=False)
    modified    = models.DateTimeField()

    def save(self, *args, **kwargs):
        ''' On save, update timestamps '''
        if not self.id:
            self.created = timezone.now()
        self.modified = timezone.now()
        return super(User, self).save(*args, **kwargs)

Hope this helps!

Edit in response to comments:

The reason why I just stick with overloading save() vs. relying on these field arguments is two-fold:

  1. The aforementioned ups and downs with their reliability. These arguments are heavily reliant on the way each type of database that Django knows how to interact with treats a date/time stamp field, and seems to break and/or change between every release. (Which I believe is the impetus behind the call to have them removed altogether).
  2. The fact that they only work on DateField, DateTimeField, and TimeField, and by using this technique you are able to automatically populate any field type every time an item is saved.
  3. Use django.utils.timezone.now() vs. datetime.datetime.now(), because it will return a TZ-aware or naive datetime.datetime object depending on settings.USE_TZ.

To address why the OP saw the error, I don’t know exactly, but it looks like created isn’t even being populated at all, despite having auto_now_add=True. To me it stands out as a bug, and underscores item #1 in my little list above: auto_now and auto_now_add are flaky at best.


回答 1

但是我想指出的是,已接受答案中表达的观点有些过时。根据最近的讨论(django bug #7634 12785),即使您进入原始讨论,auto_now和auto_now_add也不行。,您也会在自定义保存中找到针对RY的强大论点(如DRY)方法。

提供了一个更好的解决方案(自定义字段类型),但是没有获得足够的动力使其成为django。您可以三行编写自己的代码(这是Jacob Kaplan-Moss的建议)。

from django.db import models
from django.utils import timezone


class AutoDateTimeField(models.DateTimeField):
    def pre_save(self, model_instance, add):
        return timezone.now()

#usage
created_at = models.DateField(default=timezone.now)
updated_at = models.AutoDateTimeField(default=timezone.now)

But I wanted to point out that the opinion expressed in the accepted answer is somewhat outdated. According to more recent discussions (django bugs #7634 and #12785), auto_now and auto_now_add are not going anywhere, and even if you go to the original discussion, you’ll find strong arguments against the RY (as in DRY) in custom save methods.

A better solution has been offered (custom field types), but didn’t gain enough momentum to make it into django. You can write your own in three lines (it’s Jacob Kaplan-Moss’ suggestion).

from django.db import models
from django.utils import timezone


class AutoDateTimeField(models.DateTimeField):
    def pre_save(self, model_instance, add):
        return timezone.now()

#usage
created_at = models.DateField(default=timezone.now)
updated_at = models.AutoDateTimeField(default=timezone.now)

回答 2

谈论一个附带的问题:如果您想在admin中查看此字段(尽管您将无法对其进行编辑),则可以将其添加readonly_fields到admin类中。

class SomeAdmin(ModelAdmin):
    readonly_fields = ("created","modified",)

好吧,这仅适用于最新的Django版本(我相信1.3及更高版本)

Talking about a side question: if you want to see this fields in admin (though, you won’t be able to edit it), you can add readonly_fields to your admin class.

class SomeAdmin(ModelAdmin):
    readonly_fields = ("created","modified",)

Well, this applies only to latest Django versions (I believe, 1.3 and above)


回答 3

我认为这里最简单(也许也是最优雅)的解决方案是利用您可以设置default为可调用对象的事实。因此,要绕过管理员对auto_now的特殊处理,您可以像这样声明字段:

from django.utils import timezone
date_filed = models.DateField(default=timezone.now)

重要的是不要使用timezone.now()默认值,因为默认值不会更新(即,仅在加载代码时设置默认值)。如果您发现自己经常这样做,则可以创建一个自定义字段。但是,我认为这已经很干燥了。

I think the easiest (and maybe most elegant) solution here is to leverage the fact that you can set default to a callable. So, to get around admin’s special handling of auto_now, you can just declare the field like so:

from django.utils import timezone
date_filed = models.DateField(default=timezone.now)

It’s important that you don’t use timezone.now() as the default value wouldn’t update (i.e., default gets set only when the code is loaded). If you find yourself doing this a lot, you could create a custom field. However, this is pretty DRY already I think.


回答 4

如果您像这样更改模型类:

class MyModel(models.Model):
    time = models.DateTimeField(auto_now_add=True)
    time.editable = True

然后,该字段将显示在我的管理员更改页面中

If you alter your model class like this:

class MyModel(models.Model):
    time = models.DateTimeField(auto_now_add=True)
    time.editable = True

Then this field will show up in my admin change page


回答 5

根据我已经阅读的内容以及到目前为止的Django经验,auto_now_add确实存在问题。我同意詹森主义—覆盖干净的普通保存方法,您知道正在发生什么。现在,要使其干燥,请创建一个称为TimeStamped的抽象模型:

from django.utils import timezone

class TimeStamped(models.Model):
    creation_date = models.DateTimeField(editable=False)
    last_modified = models.DateTimeField(editable=False)

    def save(self, *args, **kwargs):
        if not self.creation_date:
            self.creation_date = timezone.now()

        self.last_modified = timezone.now()
        return super(TimeStamped, self).save(*args, **kwargs)

    class Meta:
        abstract = True

然后,当您想要一个具有这种耗时行为的模型时,只需子类化即可:

MyNewTimeStampyModel(TimeStamped):
    field1 = ...

如果您希望这些字段显示在admin中,则只需删除该editable=False选项

Based on what I’ve read and my experience with Django so far, auto_now_add is buggy. I agree with jthanism — override the normal save method it’s clean and you know what’s hapenning. Now, to make it dry, create an abstract model called TimeStamped:

from django.utils import timezone

class TimeStamped(models.Model):
    creation_date = models.DateTimeField(editable=False)
    last_modified = models.DateTimeField(editable=False)

    def save(self, *args, **kwargs):
        if not self.creation_date:
            self.creation_date = timezone.now()

        self.last_modified = timezone.now()
        return super(TimeStamped, self).save(*args, **kwargs)

    class Meta:
        abstract = True

And then, when you want a model that has this time-stampy behavior, just subclass:

MyNewTimeStampyModel(TimeStamped):
    field1 = ...

If you want the fields to show up in admin, then just remove the editable=False option


回答 6

这值得关注吗?

不,Django在保存模型时会自动为您添加它,因此是可以预期的。

附带问题:在我的管理工具中,这两个字段没有显示。那是预期的吗?

由于这些字段是自动添加的,因此不会显示。

正如synack所说的,除此以外,在django邮件列表上已经有辩论将其删除,因为它“设计得不好”并且是“黑客”。

与使用auto_now相比,在我的每个模型上编写自定义的save()要痛苦得多

显然,您不必将其写入每个模型。您可以将其写入一个模型并从中继承其他模型。

但是,因为auto_addauto_now_add在那里,我会用他们,而不是试图写一个方法我自己。

Is this cause for concern?

No, Django automatically adds it for you while saving the models, so, it is expected.

Side question: in my admin tool, those 2 fields aren’t showing up. Is that expected?

Since these fields are auto added, they are not shown.

To add to the above, as synack said, there has been a debate on the django mailing list to remove this, because, it is “not designed well” and is “a hack”

Writing a custom save() on each of my models is much more pain than using the auto_now

Obviously you don’t have to write it to every model. You can write it to one model and inherit others from it.

But, as auto_add and auto_now_add are there, I would use them rather than trying to write a method myself.


回答 7

今天我在工作中需要类似的东西。默认值为timezone.now(),但在继承自的管理视图和类视图中均可编辑FormMixin,因此对于在我中创建models.py的代码,以下代码满足了这些要求:

from __future__ import unicode_literals
import datetime

from django.db import models
from django.utils.functional import lazy
from django.utils.timezone import localtime, now

def get_timezone_aware_now_date():
    return localtime(now()).date()

class TestDate(models.Model):
    created = models.DateField(default=lazy(
        get_timezone_aware_now_date, datetime.date)()
    )

对于DateTimeField,我想.date()从功能中删除并更改datetime.datedatetime.datetime或更好timezone.datetime。我没有尝试过DateTime,只有尝试过Date

I needed something similar today at work. Default value to be timezone.now(), but editable both in admin and class views inheriting from FormMixin, so for created in my models.py the following code fulfilled those requirements:

from __future__ import unicode_literals
import datetime

from django.db import models
from django.utils.functional import lazy
from django.utils.timezone import localtime, now

def get_timezone_aware_now_date():
    return localtime(now()).date()

class TestDate(models.Model):
    created = models.DateField(default=lazy(
        get_timezone_aware_now_date, datetime.date)()
    )

For DateTimeField, I guess remove the .date() from the function and change datetime.date to datetime.datetime or better timezone.datetime. I haven’t tried it with DateTime, only with Date.


回答 8

您可以将其timezone.now()用于创建和auto_now修改:

from django.utils import timezone
class User(models.Model):
    created = models.DateTimeField(default=timezone.now())
    modified = models.DateTimeField(auto_now=True)

如果您使用的是自定义主键而不是默认键auto- increment intauto_now_add将导致错误。

下面是Django默认的代码DateTimeField.pre_saveauto_nowauto_now_add

def pre_save(self, model_instance, add):
    if self.auto_now or (self.auto_now_add and add):
        value = timezone.now()
        setattr(model_instance, self.attname, value)
        return value
    else:
        return super(DateTimeField, self).pre_save(model_instance, add)

我不确定参数add是什么。我希望它会像:

add = True if getattr(model_instance, 'id') else False

新记录将没有attr id,因此getattr(model_instance, 'id')返回False将导致未在字段中设置任何值。

You can use timezone.now() for created and auto_now for modified:

from django.utils import timezone
class User(models.Model):
    created = models.DateTimeField(default=timezone.now())
    modified = models.DateTimeField(auto_now=True)

If you are using a custom primary key instead of the default auto- increment int, auto_now_add will lead to a bug.

Here is the code of Django’s default DateTimeField.pre_save withauto_now and auto_now_add:

def pre_save(self, model_instance, add):
    if self.auto_now or (self.auto_now_add and add):
        value = timezone.now()
        setattr(model_instance, self.attname, value)
        return value
    else:
        return super(DateTimeField, self).pre_save(model_instance, add)

I am not sure what the parameter add is. I hope it will some thing like:

add = True if getattr(model_instance, 'id') else False

The new record will not have attr id, so getattr(model_instance, 'id') will return False will lead to not setting any value in the field.


回答 9

至于您的管理员显示,请参阅此答案

注意:auto_now并且默认auto_now_add设置为editable=False,这就是为什么这样。

As for your Admin display, see this answer.

Note: auto_now and auto_now_add are set to editable=False by default, which is why this applies.


回答 10

auto_now=True在Django 1.4.1中对我不起作用,但是以下代码救了我。用于时区感知日期时间。

from django.utils.timezone import get_current_timezone
from datetime import datetime

class EntryVote(models.Model):
    voted_on = models.DateTimeField(auto_now=True)

    def save(self, *args, **kwargs):
        self.voted_on = datetime.now().replace(tzinfo=get_current_timezone())
        super(EntryVote, self).save(*args, **kwargs)

auto_now=True didn’t work for me in Django 1.4.1, but the below code saved me. It’s for timezone aware datetime.

from django.utils.timezone import get_current_timezone
from datetime import datetime

class EntryVote(models.Model):
    voted_on = models.DateTimeField(auto_now=True)

    def save(self, *args, **kwargs):
        self.voted_on = datetime.now().replace(tzinfo=get_current_timezone())
        super(EntryVote, self).save(*args, **kwargs)

回答 11

class Feedback(models.Model):
   feedback = models.CharField(max_length=100)
   created = models.DateTimeField(auto_now_add=True)
   updated = models.DateTimeField(auto_now=True)

在这里,我们创建并更新了列,这些列在创建时以及有人修改反馈时都会带有时间戳。

auto_now_add将设置创建实例的时间,而auto_now将设置某人修改其反馈的时间。

class Feedback(models.Model):
   feedback = models.CharField(max_length=100)
   created = models.DateTimeField(auto_now_add=True)
   updated = models.DateTimeField(auto_now=True)

Here, we have created and updated columns that will have a timestamp when created, and when someone modified feedback.

auto_now_add will set time when an instance is created whereas auto_now will set time when someone modified his feedback.


回答 12

如果您使用的是南方,并且想要默认为将字段添加到数据库的日期,这就是答案:

选择选项2, 然后: datetime.datetime.now()

看起来像这样:

$ ./manage.py schemamigration myapp --auto
 ? The field 'User.created_date' does not have a default specified, yet is NOT NULL.
 ? Since you are adding this field, you MUST specify a default
 ? value to use for existing rows. Would you like to:
 ?  1. Quit now, and add a default to the field in models.py
 ?  2. Specify a one-off value to use for existing columns now
 ? Please select a choice: 2
 ? Please enter Python code for your one-off default value.
 ? The datetime module is available, so you can do e.g. datetime.date.today()
 >>> datetime.datetime.now()
 + Added field created_date on myapp.User

Here’s the answer if you’re using south and you want to default to the date you add the field to the database:

Choose option 2 then: datetime.datetime.now()

Looks like this:

$ ./manage.py schemamigration myapp --auto
 ? The field 'User.created_date' does not have a default specified, yet is NOT NULL.
 ? Since you are adding this field, you MUST specify a default
 ? value to use for existing rows. Would you like to:
 ?  1. Quit now, and add a default to the field in models.py
 ?  2. Specify a one-off value to use for existing columns now
 ? Please select a choice: 2
 ? Please enter Python code for your one-off default value.
 ? The datetime module is available, so you can do e.g. datetime.date.today()
 >>> datetime.datetime.now()
 + Added field created_date on myapp.User

Django Model()与Model.objects.create()

问题:Django Model()与Model.objects.create()

运行两个命令有什么区别:

foo = FooModel()

bar = BarModel.objects.create()

第二个方法是否立即BarModel在数据库中创建一个,而对于FooModelsave()必须显式调用该方法以将其添加到数据库中?

What it the difference between running two commands:

foo = FooModel()

and

bar = BarModel.objects.create()

Does the second one immediately create a BarModel in the database, while for FooModel, the save() method has to be called explicitly to add it to the database?


回答 0

https://docs.djangoproject.com/zh-CN/stable/topics/db/queries/#creating-objects

要在一个步骤中创建和保存对象,请使用create()方法。

https://docs.djangoproject.com/en/stable/topics/db/queries/#creating-objects

To create and save an object in a single step, use the create() method.


回答 1

两种语法不等效,并且可能导致意外错误。这是一个显示差异的简单示例。如果您有模型:

from django.db import models

class Test(models.Model):

    added = models.DateTimeField(auto_now_add=True)

然后创建第一个对象:

foo = Test.objects.create(pk=1)

然后尝试使用相同的主键创建一个对象:

foo_duplicate = Test.objects.create(pk=1)
# returns the error:
# django.db.utils.IntegrityError: (1062, "Duplicate entry '1' for key 'PRIMARY'")

foo_duplicate = Test(pk=1).save()
# returns the error:
# django.db.utils.IntegrityError: (1048, "Column 'added' cannot be null")

The two syntaxes are not equivalent and it can lead to unexpected errors. Here is a simple example showing the differences. If you have a model:

from django.db import models

class Test(models.Model):

    added = models.DateTimeField(auto_now_add=True)

And you create a first object:

foo = Test.objects.create(pk=1)

Then you try to create an object with the same primary key:

foo_duplicate = Test.objects.create(pk=1)
# returns the error:
# django.db.utils.IntegrityError: (1062, "Duplicate entry '1' for key 'PRIMARY'")

foo_duplicate = Test(pk=1).save()
# returns the error:
# django.db.utils.IntegrityError: (1048, "Column 'added' cannot be null")

回答 2

更新15.3.2017:

我已经对此打开了Django问题,似乎已经在这里被初步接受:https : //code.djangoproject.com/ticket/27825

我的经验是,当通过Django 在引用中使用ConstructorORM)类时,1.10.5数据中可能存在一些不一致(即,创建对象的属性可能获取输入数据的类型,而不是ORM对象属性的强制类型)。 :

models

class Payment(models.Model):
     amount_cash = models.DecimalField()

some_test.pyobject.create

Class SomeTestCase:
    def generate_orm_obj(self, _constructor, base_data=None, modifiers=None):
        objs = []
        if not base_data:
            base_data = {'amount_case': 123.00}
        for modifier in modifiers:
            actual_data = deepcopy(base_data)
            actual_data.update(modifier)
            # Hacky fix,
            _obj = _constructor.objects.create(**actual_data)
            print(type(_obj.amount_cash)) # Decimal
            assert created
           objs.append(_obj)
        return objs

some_test.pyConstructor()

Class SomeTestCase:
    def generate_orm_obj(self, _constructor, base_data=None, modifiers=None):
        objs = []
        if not base_data:
            base_data = {'amount_case': 123.00}
        for modifier in modifiers:
            actual_data = deepcopy(base_data)
            actual_data.update(modifier)
            # Hacky fix,
            _obj = _constructor(**actual_data)
            print(type(_obj.amount_cash)) # Float
            assert created
           objs.append(_obj)
        return objs

UPDATE 15.3.2017:

I have opened a Django-issue on this and it seems to be preliminary accepted here: https://code.djangoproject.com/ticket/27825

My experience is that when using the Constructor (ORM) class by references with Django 1.10.5 there might be some inconsistencies in the data (i.e. the attributes of the created object may get the type of the input data instead of the casted type of the ORM object property) example:

models

class Payment(models.Model):
     amount_cash = models.DecimalField()

some_test.pyobject.create

Class SomeTestCase:
    def generate_orm_obj(self, _constructor, base_data=None, modifiers=None):
        objs = []
        if not base_data:
            base_data = {'amount_case': 123.00}
        for modifier in modifiers:
            actual_data = deepcopy(base_data)
            actual_data.update(modifier)
            # Hacky fix,
            _obj = _constructor.objects.create(**actual_data)
            print(type(_obj.amount_cash)) # Decimal
            assert created
           objs.append(_obj)
        return objs

some_test.pyConstructor()

Class SomeTestCase:
    def generate_orm_obj(self, _constructor, base_data=None, modifiers=None):
        objs = []
        if not base_data:
            base_data = {'amount_case': 123.00}
        for modifier in modifiers:
            actual_data = deepcopy(base_data)
            actual_data.update(modifier)
            # Hacky fix,
            _obj = _constructor(**actual_data)
            print(type(_obj.amount_cash)) # Float
            assert created
           objs.append(_obj)
        return objs

如何在Django模型中删除记录?

问题:如何在Django模型中删除记录?

我要删除特定记录。如

delete from table_name where id = 1;

我怎么能做到这一点django model

I want to delete a particular record. Such as

delete from table_name where id = 1;

How can I do this in a django model?


回答 0

有两种方法:

要直接删除它:

SomeModel.objects.filter(id=id).delete()

要从实例中删除它:

instance = SomeModel.objects.get(id=id)
instance.delete()

There are a couple of ways:

To delete it directly:

SomeModel.objects.filter(id=id).delete()

To delete it from an instance:

instance = SomeModel.objects.get(id=id)
instance.delete()

回答 1

MyModel.objects.get(pk=1).delete()

如果具有指定主键的对象不存在,这将引发异常,因为它首先会尝试检索指定的对象。

MyModel.objects.filter(pk=1).delete()

如果具有指定主键的对象不存在,并且不会直接产生查询,则不会引发异常

DELETE FROM my_models where id=1
MyModel.objects.get(pk=1).delete()

this will raise exception if the object with specified primary key doesn’t exist because at first it tries to retrieve the specified object.

MyModel.objects.filter(pk=1).delete()

this wont raise exception if the object with specified primary key doesn’t exist and it directly produces the query

DELETE FROM my_models where id=1

回答 2

如果要删除一项

wishlist = Wishlist.objects.get(id = 20)
wishlist.delete()

例如,如果要删除收藏夹中的所有项目

Wishlist.objects.all().delete()

If you want to delete one item

wishlist = Wishlist.objects.get(id = 20)
wishlist.delete()

If you want to delete all items in Wishlist for example

Wishlist.objects.all().delete()

回答 3

如果要删除一个实例,请编写代码

delet= Account.objects.get(id= 5)
delet.delete()

如果要删除所有实例,请编写代码

delet= Account.objects.all()
delete.delete()

if you want to delete one instance then write the code

entry= Account.objects.get(id= 5)
entry.delete()

if you want to delete all instance then write the code

entries= Account.objects.all()
entries.delete()

回答 4

沃尔夫提供了一个很好的答案集中代码。让我在这里粘贴官方文档,以供大家参考。

Wolph provided a good answer focused codes. Let me just paste official doc here, for people’s reference.


如何克隆Django模型实例对象并将其保存到数据库?

问题:如何克隆Django模型实例对象并将其保存到数据库?

Foo.objects.get(pk="foo")
<Foo: test>

在数据库中,我想添加另一个对象,它是上述对象的副本。

假设我的桌子有一排。我想用不同的主键将第一行对象插入另一行。我怎样才能做到这一点?

Foo.objects.get(pk="foo")
<Foo: test>

In the database, I want to add another object which is a copy of the object above.

Suppose my table has one row. I want to insert the first row object into another row with a different primary key. How can I do that?


回答 0

只需更改对象的主键并运行save()。

obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()

如果要自动生成密钥,请将新密钥设置为“无”。

有关UPDATE / INSERT的更多信息,请点击这里

有关复制模型实例的官方文档:https : //docs.djangoproject.com/en/2.2/topics/db/queries/#copying-model-instances

Just change the primary key of your object and run save().

obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()

If you want auto-generated key, set the new key to None.

More on UPDATE/INSERT here.

Official docs on copying model instances: https://docs.djangoproject.com/en/2.2/topics/db/queries/#copying-model-instances


回答 1

Django数据库查询文档包括有关复制模型实例的部分。假设您的主键是自动生成的,则得到要复制的对象,将主键设置为None,然后再次保存该对象:

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2

在此片段中,第一个save()创建原始对象,第二个save()创建副本。

如果您继续阅读文档,那么还会有一些示例,说明如何处理两种更复杂的情况:(1)复制一个对象,该对象是模型子类的一个实例,(2)还复制相关对象,包括多对多对象-很多关系。


请注意miah的答案:miah的答案None中提到了将pk设置为,尽管未将其设置在前面和中间。因此,我的回答主要是为了强调该方法,这是Django推荐的方法。

历史记录:Django文档直到1.4版才对此进行了解释。但是从1.4之前开始就有可能。

将来可能的功能:对该票证进行了上述文档更改。在故障单的注释线程上,还讨论了copy为模型类添加内置函数的问题,但据我所知,他们决定不解决该问题。因此,这种“手动”复制方式现在可能必须要做。

The Django documentation for database queries includes a section on copying model instances. Assuming your primary keys are autogenerated, you get the object you want to copy, set the primary key to None, and save the object again:

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2

In this snippet, the first save() creates the original object, and the second save() creates the copy.

If you keep reading the documentation, there are also examples on how to handle two more complex cases: (1) copying an object which is an instance of a model subclass, and (2) also copying related objects, including objects in many-to-many relations.


Note on miah’s answer: Setting the pk to None is mentioned in miah’s answer, although it’s not presented front and center. So my answer mainly serves to emphasize that method as the Django-recommended way to do it.

Historical note: This wasn’t explained in the Django docs until version 1.4. It has been possible since before 1.4, though.

Possible future functionality: The aforementioned docs change was made in this ticket. On the ticket’s comment thread, there was also some discussion on adding a built-in copy function for model classes, but as far as I know they decided not to tackle that problem yet. So this “manual” way of copying will probably have to do for now.


回答 2

小心点 如果您处于某种循环中,并且要一个接一个地检索对象,这可能会非常昂贵。如果您不想调用数据库,请执行以下操作:

from copy import deepcopy

new_instance = deepcopy(object_you_want_copied)
new_instance.id = None
new_instance.save()

它与其他一些答案具有相同的作用,但是不会进行数据库调用来检索对象。如果您要复制数据库中尚不存在的对象,这也很有用。

Be careful here. This can be extremely expensive if you’re in a loop of some kind and you’re retrieving objects one by one. If you don’t want the call to the database, just do:

from copy import deepcopy

new_instance = deepcopy(object_you_want_copied)
new_instance.id = None
new_instance.save()

It does the same thing as some of these other answers, but it doesn’t make the database call to retrieve an object. This is also useful if you want to make a copy of an object that doesn’t exist yet in the database.


回答 3

使用以下代码:

from django.forms import model_to_dict

instance = Some.objects.get(slug='something')

kwargs = model_to_dict(instance, exclude=['id'])
new_instance = Some.objects.create(**kwargs)

Use the below code :

from django.forms import model_to_dict

instance = Some.objects.get(slug='something')

kwargs = model_to_dict(instance, exclude=['id'])
new_instance = Some.objects.create(**kwargs)

回答 4

有一个克隆片段在这里,你可以添加到您的模型,做到这一点:

def clone(self):
  new_kwargs = dict([(fld.name, getattr(old, fld.name)) for fld in old._meta.fields if fld.name != old._meta.pk]);
  return self.__class__.objects.create(**new_kwargs)

There’s a clone snippet here, which you can add to your model which does this:

def clone(self):
  new_kwargs = dict([(fld.name, getattr(old, fld.name)) for fld in old._meta.fields if fld.name != old._meta.pk]);
  return self.__class__.objects.create(**new_kwargs)

回答 5

如何执行此操作已添加到Django1.4中的官方Django文档中

https://docs.djangoproject.com/en/1.10/topics/db/queries/#copying-model-instances

官方答案与miah的答案相似,但是文档指出了继承和相关对象方面的一些困难,因此您可能应该确保已阅读文档。

How to do this was added to the official Django docs in Django1.4

https://docs.djangoproject.com/en/1.10/topics/db/queries/#copying-model-instances

The official answer is similar to miah’s answer, but the docs point out some difficulties with inheritance and related objects, so you should probably make sure you read the docs.


回答 6

我遇到了一些公认的答案。这是我的解决方案。

import copy

def clone(instance):
    cloned = copy.copy(instance) # don't alter original instance
    cloned.pk = None
    try:
        delattr(cloned, '_prefetched_objects_cache')
    except AttributeError:
        pass
    return cloned

注意:这使用的解决方案未在Django文档中得到正式批准,并且在以后的版本中可能会停止使用。我在1.9.13中进行了测试。

第一个改进是,它允许您通过使用继续使用原始实例copy.copy。即使您不打算重用该实例,如果要克隆的实例作为参数传递给函数,执行此步骤也可能更安全。否则,函数返回时,调用方将意外地拥有其他实例。

copy.copy似乎以所需的方式生成了Django模型实例的浅表副本。这是我未发现的东西之一,但是它可以通过酸洗和酸洗来工作,因此可能得到了很好的支持。

其次,批准的答案将把任何预取结果附加到新实例上。除非您明确复制多对多关系,否则这些结果不应与新实例相关联。如果遍历预取的关系,将得到与数据库不匹配的结果。添加预取时破坏工作代码可能会令人讨厌。

删除_prefetched_objects_cache是一种剥离所有预取的快捷方法。随后的许多访问就像从未进行过预取一样工作。使用以下划线开头的未记录属性可能会引起兼容性问题,但现在可以使用。

I’ve run into a couple gotchas with the accepted answer. Here is my solution.

import copy

def clone(instance):
    cloned = copy.copy(instance) # don't alter original instance
    cloned.pk = None
    try:
        delattr(cloned, '_prefetched_objects_cache')
    except AttributeError:
        pass
    return cloned

Note: this uses solutions that aren’t officially sanctioned in the Django docs, and they may cease to work in future versions. I tested this in 1.9.13.

The first improvement is that it allows you to continue using the original instance, by using copy.copy. Even if you don’t intend to reuse the instance, it can be safer to do this step if the instance you’re cloning was passed as an argument to a function. If not, the caller will unexpectedly have a different instance when the function returns.

copy.copy seems to produce a shallow copy of a Django model instance in the desired way. This is one of the things I did not find documented, but it works by pickling and unpickling, so it’s probably well-supported.

Secondly, the approved answer will leave any prefetched results attached to the new instance. Those results shouldn’t be associated with the new instance, unless you explicitly copy the to-many relationships. If you traverse the the prefetched relationships, you will get results that don’t match the database. Breaking working code when you add a prefetch can be a nasty surprise.

Deleting _prefetched_objects_cache is a quick-and-dirty way to strip away all prefetches. Subsequent to-many accesses work as if there never was a prefetch. Using an undocumented property that begins with an underscore is probably asking for compatibility trouble, but it works for now.


回答 7

将pk设置为None更好,Sinse Django可以为您正确创建一个pk

object_copy = MyObject.objects.get(pk=...)
object_copy.pk = None
object_copy.save()

setting pk to None is better, sinse Django can correctly create a pk for you

object_copy = MyObject.objects.get(pk=...)
object_copy.pk = None
object_copy.save()

回答 8

这是克隆模型实例的另一种方法:

d = Foo.objects.filter(pk=1).values().first()   
d.update({'id': None})
duplicate = Foo.objects.create(**d)

This is yet another way of cloning the model instance:

d = Foo.objects.filter(pk=1).values().first()   
d.update({'id': None})
duplicate = Foo.objects.create(**d)

回答 9

要克隆具有多个继承级别(即> = 2或下面的ModelC)的模型

class ModelA(models.Model):
    info1 = models.CharField(max_length=64)

class ModelB(ModelA):
    info2 = models.CharField(max_length=64)

class ModelC(ModelB):
    info3 = models.CharField(max_length=64)

请在这里参考问题。

To clone a model with multiple inheritance levels, i.e. >= 2, or ModelC below

class ModelA(models.Model):
    info1 = models.CharField(max_length=64)

class ModelB(ModelA):
    info2 = models.CharField(max_length=64)

class ModelC(ModelB):
    info3 = models.CharField(max_length=64)

Please refer the question here.


回答 10

试试这个

original_object = Foo.objects.get(pk="foo")
v = vars(original_object)
v.pop("pk")
new_object = Foo(**v)
new_object.save()

Try this

original_object = Foo.objects.get(pk="foo")
v = vars(original_object)
v.pop("pk")
new_object = Foo(**v)
new_object.save()

将Django模型对象转换为所有字段均完整的dict

问题:将Django模型对象转换为所有字段均完整的dict

如何将Django模型对象转换为具有所有字段的字典?理想情况下,所有内容都包含带有的外键和字段editable=False

让我详细说明。假设我有一个类似以下的Django模型:

from django.db import models

class OtherModel(models.Model): pass

class SomeModel(models.Model):
    normal_value = models.IntegerField()
    readonly_value = models.IntegerField(editable=False)
    auto_now_add = models.DateTimeField(auto_now_add=True)
    foreign_key = models.ForeignKey(OtherModel, related_name="ref1")
    many_to_many = models.ManyToManyField(OtherModel, related_name="ref2")

在终端中,我已执行以下操作:

other_model = OtherModel()
other_model.save()
instance = SomeModel()
instance.normal_value = 1
instance.readonly_value = 2
instance.foreign_key = other_model
instance.save()
instance.many_to_many.add(other_model)
instance.save()

我想将其转换为以下字典:

{'auto_now_add': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
 'foreign_key': 1,
 'id': 1,
 'many_to_many': [1],
 'normal_value': 1,
 'readonly_value': 2}

答案不令人满意的问题:

Django:将整个模型对象集转换为单个字典

如何将Django模型对象转换为字典并仍然具有其外键?

How does one convert a Django Model object to a dict with all of its fields? All ideally includes foreign keys and fields with editable=False.

Let me elaborate. Let’s say I have a Django model like the following:

from django.db import models

class OtherModel(models.Model): pass

class SomeModel(models.Model):
    normal_value = models.IntegerField()
    readonly_value = models.IntegerField(editable=False)
    auto_now_add = models.DateTimeField(auto_now_add=True)
    foreign_key = models.ForeignKey(OtherModel, related_name="ref1")
    many_to_many = models.ManyToManyField(OtherModel, related_name="ref2")

In the terminal, I have done the following:

other_model = OtherModel()
other_model.save()
instance = SomeModel()
instance.normal_value = 1
instance.readonly_value = 2
instance.foreign_key = other_model
instance.save()
instance.many_to_many.add(other_model)
instance.save()

I want to convert this to the following dictionary:

{'auto_now_add': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
 'foreign_key': 1,
 'id': 1,
 'many_to_many': [1],
 'normal_value': 1,
 'readonly_value': 2}

Questions with unsatisfactory answers:

Django: Converting an entire set of a Model’s objects into a single dictionary

How can I turn Django Model objects into a dictionary and still have their foreign keys?


回答 0

有多种方法可将实例转换为字典,并具有不同程度的特殊情况处理和接近所需结果的程度。


1。 instance.__dict__

instance.__dict__

哪个返回

{'_foreign_key_cache': <OtherModel: OtherModel object>,
 '_state': <django.db.models.base.ModelState at 0x7ff0993f6908>,
 'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key_id': 2,
 'id': 1,
 'normal_value': 1,
 'readonly_value': 2}

到目前为止,这是最简单的方法,但是缺少many_to_manyforeign_key被错误命名,并且其中有两个多余的多余内容。


2。 model_to_dict

from django.forms.models import model_to_dict
model_to_dict(instance)

哪个返回

{'foreign_key': 2,
 'id': 1,
 'many_to_many': [<OtherModel: OtherModel object>],
 'normal_value': 1}

这是唯一的many_to_many,但缺少不可编辑的字段。


3。 model_to_dict(..., fields=...)

from django.forms.models import model_to_dict
model_to_dict(instance, fields=[field.name for field in instance._meta.fields])

哪个返回

{'foreign_key': 2, 'id': 1, 'normal_value': 1}

这绝对比标准model_to_dict调用差。


4。 query_set.values()

SomeModel.objects.filter(id=instance.id).values()[0]

哪个返回

{'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key_id': 2,
 'id': 1,
 'normal_value': 1,
 'readonly_value': 2}

这与输出相同,instance.__dict__但没有额外的字段。 foreign_key_id仍然是错误的,many_to_many仍然不见了。


5.自定义功能

django的代码model_to_dict具有大部分答案。它显式删除了不可编辑的字段,因此删除该检查并获取多对多字段的外键ID会导致以下代码按预期运行:

from itertools import chain

def to_dict(instance):
    opts = instance._meta
    data = {}
    for f in chain(opts.concrete_fields, opts.private_fields):
        data[f.name] = f.value_from_object(instance)
    for f in opts.many_to_many:
        data[f.name] = [i.id for i in f.value_from_object(instance)]
    return data

虽然这是最复杂的选项,但调用to_dict(instance)会给我们确切的预期结果:

{'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

6.使用序列化器

Django Rest Framework的ModelSerialzer允许您从模型自动构建序列化器。

from rest_framework import serializers
class SomeModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = SomeModel
        fields = "__all__"

SomeModelSerializer(instance).data

退货

{'auto_now_add': '2018-12-20T21:34:29.494827Z',
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

这几乎与自定义函数一样好,但是auto_now_add是字符串而不是datetime对象。


奖金回合:更好的模型印刷

如果您想要一个具有更好的python命令行显示的Django模型,请让您的模型将以下子类:

from django.db import models
from itertools import chain

class PrintableModel(models.Model):
    def __repr__(self):
        return str(self.to_dict())

    def to_dict(instance):
        opts = instance._meta
        data = {}
        for f in chain(opts.concrete_fields, opts.private_fields):
            data[f.name] = f.value_from_object(instance)
        for f in opts.many_to_many:
            data[f.name] = [i.id for i in f.value_from_object(instance)]
        return data

    class Meta:
        abstract = True

因此,例如,如果我们这样定义模型:

class OtherModel(PrintableModel): pass

class SomeModel(PrintableModel):
    normal_value = models.IntegerField()
    readonly_value = models.IntegerField(editable=False)
    auto_now_add = models.DateTimeField(auto_now_add=True)
    foreign_key = models.ForeignKey(OtherModel, related_name="ref1")
    many_to_many = models.ManyToManyField(OtherModel, related_name="ref2")

SomeModel.objects.first()现在调用将产生如下输出:

{'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

There are many ways to convert an instance to a dictionary, with varying degrees of corner case handling and closeness to the desired result.


1. instance.__dict__

instance.__dict__

which returns

{'_foreign_key_cache': <OtherModel: OtherModel object>,
 '_state': <django.db.models.base.ModelState at 0x7ff0993f6908>,
 'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key_id': 2,
 'id': 1,
 'normal_value': 1,
 'readonly_value': 2}

This is by far the simplest, but is missing many_to_many, foreign_key is misnamed, and it has two unwanted extra things in it.


2. model_to_dict

from django.forms.models import model_to_dict
model_to_dict(instance)

which returns

{'foreign_key': 2,
 'id': 1,
 'many_to_many': [<OtherModel: OtherModel object>],
 'normal_value': 1}

This is the only one with many_to_many, but is missing the uneditable fields.


3. model_to_dict(..., fields=...)

from django.forms.models import model_to_dict
model_to_dict(instance, fields=[field.name for field in instance._meta.fields])

which returns

{'foreign_key': 2, 'id': 1, 'normal_value': 1}

This is strictly worse than the standard model_to_dict invocation.


4. query_set.values()

SomeModel.objects.filter(id=instance.id).values()[0]

which returns

{'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key_id': 2,
 'id': 1,
 'normal_value': 1,
 'readonly_value': 2}

This is the same output as instance.__dict__ but without the extra fields. foreign_key_id is still wrong and many_to_many is still missing.


5. Custom Function

The code for django’s model_to_dict had most of the answer. It explicitly removed non-editable fields, so removing that check and getting the ids of foreign keys for many to many fields results in the following code which behaves as desired:

from itertools import chain

def to_dict(instance):
    opts = instance._meta
    data = {}
    for f in chain(opts.concrete_fields, opts.private_fields):
        data[f.name] = f.value_from_object(instance)
    for f in opts.many_to_many:
        data[f.name] = [i.id for i in f.value_from_object(instance)]
    return data

While this is the most complicated option, calling to_dict(instance) gives us exactly the desired result:

{'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

6. Use Serializers

Django Rest Framework‘s ModelSerialzer allows you to build a serializer automatically from a model.

from rest_framework import serializers
class SomeModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = SomeModel
        fields = "__all__"

SomeModelSerializer(instance).data

returns

{'auto_now_add': '2018-12-20T21:34:29.494827Z',
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

This is almost as good as the custom function, but auto_now_add is a string instead of a datetime object.


Bonus Round: better model printing

If you want a django model that has a better python command-line display, have your models child-class the following:

from django.db import models
from itertools import chain

class PrintableModel(models.Model):
    def __repr__(self):
        return str(self.to_dict())

    def to_dict(instance):
        opts = instance._meta
        data = {}
        for f in chain(opts.concrete_fields, opts.private_fields):
            data[f.name] = f.value_from_object(instance)
        for f in opts.many_to_many:
            data[f.name] = [i.id for i in f.value_from_object(instance)]
        return data

    class Meta:
        abstract = True

So, for example, if we define our models as such:

class OtherModel(PrintableModel): pass

class SomeModel(PrintableModel):
    normal_value = models.IntegerField()
    readonly_value = models.IntegerField(editable=False)
    auto_now_add = models.DateTimeField(auto_now_add=True)
    foreign_key = models.ForeignKey(OtherModel, related_name="ref1")
    many_to_many = models.ManyToManyField(OtherModel, related_name="ref2")

Calling SomeModel.objects.first() now gives output like this:

{'auto_now_add': datetime.datetime(2018, 12, 20, 21, 34, 29, 494827, tzinfo=<UTC>),
 'foreign_key': 2,
 'id': 1,
 'many_to_many': [2],
 'normal_value': 1,
 'readonly_value': 2}

回答 1

我找到了一个整洁的解决方案以得到结果:

假设您有一个模型对象o

只需调用:

type(o).objects.filter(pk=o.pk).values().first()

I found a neat solution to get to result:

Suppose you have an model object o:

Just call:

type(o).objects.filter(pk=o.pk).values().first()

回答 2

@Zags解决方案很棒!

不过,我将为datefields添加一个条件,以使其对JSON友好。

奖金回合

如果您希望Django模型具有更好的python命令行显示,请让您的模型子类具有以下功能:

from django.db import models
from django.db.models.fields.related import ManyToManyField

class PrintableModel(models.Model):
    def __repr__(self):
        return str(self.to_dict())

    def to_dict(self):
        opts = self._meta
        data = {}
        for f in opts.concrete_fields + opts.many_to_many:
            if isinstance(f, ManyToManyField):
                if self.pk is None:
                    data[f.name] = []
                else:
                    data[f.name] = list(f.value_from_object(self).values_list('pk', flat=True))
            elif isinstance(f, DateTimeField):
                if f.value_from_object(self) is not None:
                    data[f.name] = f.value_from_object(self).timestamp()
            else:
                data[f.name] = None
            else:
                data[f.name] = f.value_from_object(self)
        return data

    class Meta:
        abstract = True

因此,例如,如果我们这样定义模型:

class OtherModel(PrintableModel): pass

class SomeModel(PrintableModel):
    value = models.IntegerField()
    value2 = models.IntegerField(editable=False)
    created = models.DateTimeField(auto_now_add=True)
    reference1 = models.ForeignKey(OtherModel, related_name="ref1")
    reference2 = models.ManyToManyField(OtherModel, related_name="ref2")

SomeModel.objects.first()现在调用将产生如下输出:

{'created': 1426552454.926738,
'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}

@Zags solution was gorgeous!

I would add, though, a condition for datefields in order to make it JSON friendly.

Bonus Round

If you want a django model that has a better python command-line display, have your models child class the following:

from django.db import models
from django.db.models.fields.related import ManyToManyField

class PrintableModel(models.Model):
    def __repr__(self):
        return str(self.to_dict())

    def to_dict(self):
        opts = self._meta
        data = {}
        for f in opts.concrete_fields + opts.many_to_many:
            if isinstance(f, ManyToManyField):
                if self.pk is None:
                    data[f.name] = []
                else:
                    data[f.name] = list(f.value_from_object(self).values_list('pk', flat=True))
            elif isinstance(f, DateTimeField):
                if f.value_from_object(self) is not None:
                    data[f.name] = f.value_from_object(self).timestamp()
            else:
                data[f.name] = None
            else:
                data[f.name] = f.value_from_object(self)
        return data

    class Meta:
        abstract = True

So, for example, if we define our models as such:

class OtherModel(PrintableModel): pass

class SomeModel(PrintableModel):
    value = models.IntegerField()
    value2 = models.IntegerField(editable=False)
    created = models.DateTimeField(auto_now_add=True)
    reference1 = models.ForeignKey(OtherModel, related_name="ref1")
    reference2 = models.ManyToManyField(OtherModel, related_name="ref2")

Calling SomeModel.objects.first() now gives output like this:

{'created': 1426552454.926738,
'value': 1, 'value2': 2, 'reference1': 1, u'id': 1, 'reference2': [1]}

回答 3

最简单的方法

  1. 如果您的查询是Model.Objects.get():

    get()将返回单个实例,因此您可以直接__dict__从实例中使用

    model_dict = Model.Objects.get().__dict__

  2. 对于filter()/ all():

    all()/ filter()将返回实例列表,因此您可以values()用来获取对象列表。

    model_values = Model.Objects.all()。values()

Simplest way,

  1. If your query is Model.Objects.get():

    get() will return single instance so you can direct use __dict__ from your instance

    model_dict = Model.Objects.get().__dict__

  2. for filter()/all():

    all()/filter() will return list of instances so you can use values() to get list of objects.

    model_values = Model.Objects.all().values()


回答 4

只是vars(obj),它将说明对象的整个值

>>> obj_attrs = vars(obj)
>>> obj_attrs
 {'_file_data_cache': <FileData: Data>,
  '_state': <django.db.models.base.ModelState at 0x7f5c6733bad0>,
  'aggregator_id': 24,
  'amount': 5.0,
  'biller_id': 23,
  'datetime': datetime.datetime(2018, 1, 31, 18, 43, 58, 933277, tzinfo=<UTC>),
  'file_data_id': 797719,
 }

您也可以添加

>>> keys = obj_attrs.keys()
>>> temp = [obj_attrs.pop(key) if key.startswith('_') else None for key in keys]
>>> del temp
>>> obj_attrs
   {
    'aggregator_id': 24,
    'amount': 5.0,
    'biller_id': 23,
    'datetime': datetime.datetime(2018, 1, 31, 18, 43, 58, 933277, tzinfo=<UTC>),
    'file_data_id': 797719,
   }

just vars(obj) , it will state the whole values of the object

>>> obj_attrs = vars(obj)
>>> obj_attrs
 {'_file_data_cache': <FileData: Data>,
  '_state': <django.db.models.base.ModelState at 0x7f5c6733bad0>,
  'aggregator_id': 24,
  'amount': 5.0,
  'biller_id': 23,
  'datetime': datetime.datetime(2018, 1, 31, 18, 43, 58, 933277, tzinfo=<UTC>),
  'file_data_id': 797719,
 }

You can add this also

>>> keys = obj_attrs.keys()
>>> temp = [obj_attrs.pop(key) if key.startswith('_') else None for key in keys]
>>> del temp
>>> obj_attrs
   {
    'aggregator_id': 24,
    'amount': 5.0,
    'biller_id': 23,
    'datetime': datetime.datetime(2018, 1, 31, 18, 43, 58, 933277, tzinfo=<UTC>),
    'file_data_id': 797719,
   }

回答 5

更新资料

@zags发布的较新的汇总答案比我自己的答案更完整,更优雅。请改为参考该答案。

原版的

如果您愿意像@karthiker建议的那样定义自己的to_dict方法,那么就可以将此问题归结为集合问题。

>>># Returns a set of all keys excluding editable = False keys
>>>dict = model_to_dict(instance)
>>>dict

{u'id': 1L, 'reference1': 1L, 'reference2': [1L], 'value': 1}


>>># Returns a set of editable = False keys, misnamed foreign keys, and normal keys
>>>otherDict = SomeModel.objects.filter(id=instance.id).values()[0]
>>>otherDict

{'created': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>),
 u'id': 1L,
 'reference1_id': 1L,
 'value': 1L,
 'value2': 2L}

我们需要从otherDict中删除贴标签的外键。

为此,我们可以使用一个循环来创建一个新字典,该字典除了包含下划线的项外,还包含所有项。或者,为了节省时间,我们可以将它们添加到原始字典中,因为字典只是在幕后设置的。

>>>for item in otherDict.items():
...    if "_" not in item[0]:
...            dict.update({item[0]:item[1]})
...
>>>

因此,我们只能用下面的字典

>>>dict
{'created': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>),
 u'id': 1L,
 'reference1': 1L,
 'reference2': [1L],
 'value': 1,
 'value2': 2L}

然后您将其退回。

不利的一面是,您不能在editable = false字段名称中使用下划线。从好的方面来说,这将适用于用户创建的字段不包含下划线的任何字段集。

这不是执行此操作的最佳方法,但是在找到更直接的方法之前,它可以作为临时解决方案。

对于以下示例,将基于model_to_dict形成dict,并通过filter的values方法形成otherDict。我本来可以用模型自己完成的,但是我无法让我的机器接受otherModel。

>>> import datetime
>>> dict = {u'id': 1, 'reference1': 1, 'reference2': [1], 'value': 1}
>>> otherDict = {'created': datetime.datetime(2014, 2, 21, 4, 38, 51), u'id': 1, 'reference1_id': 1, 'value': 1, 'value2': 2}
>>> for item in otherDict.items():
...     if "_" not in item[0]:
...             dict.update({item[0]:item[1]})
...
>>> dict
{'reference1': 1, 'created': datetime.datetime(2014, 2, 21, 4, 38, 51), 'value2': 2, 'value': 1, 'id': 1, 'reference2': [1]}
>>>

我希望,这应该使您对问题的答案有个大概的了解。

Update

The newer aggregated answer posted by @zags is more complete and elegant than my own. Please refer to that answer instead.

Original

If you are willing to define your own to_dict method like @karthiker suggested, then that just boils this problem down to a sets problem.

>>># Returns a set of all keys excluding editable = False keys
>>>dict = model_to_dict(instance)
>>>dict

{u'id': 1L, 'reference1': 1L, 'reference2': [1L], 'value': 1}


>>># Returns a set of editable = False keys, misnamed foreign keys, and normal keys
>>>otherDict = SomeModel.objects.filter(id=instance.id).values()[0]
>>>otherDict

{'created': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>),
 u'id': 1L,
 'reference1_id': 1L,
 'value': 1L,
 'value2': 2L}

We need to remove the mislabeled foreign keys from otherDict.

To do this, we can use a loop that makes a new dictionary that has every item except those with underscores in them. Or, to save time, we can just add those to the original dict since dictionaries are just sets under the hood.

>>>for item in otherDict.items():
...    if "_" not in item[0]:
...            dict.update({item[0]:item[1]})
...
>>>

Thus we are left with the following dict:

>>>dict
{'created': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>),
 u'id': 1L,
 'reference1': 1L,
 'reference2': [1L],
 'value': 1,
 'value2': 2L}

And you just return that.

On the downside, you can’t use underscores in your editable=false field names. On the upside, this will work for any set of fields where the user-made fields do not contain underscores.

This is not the best way of doing this, but it could work as a temporary solution until a more direct method is found.

For the example below, dict would be formed based on model_to_dict and otherDict would be formed by filter’s values method. I would have done this with the models themselves, but I can’t get my machine to accept otherModel.

>>> import datetime
>>> dict = {u'id': 1, 'reference1': 1, 'reference2': [1], 'value': 1}
>>> otherDict = {'created': datetime.datetime(2014, 2, 21, 4, 38, 51), u'id': 1, 'reference1_id': 1, 'value': 1, 'value2': 2}
>>> for item in otherDict.items():
...     if "_" not in item[0]:
...             dict.update({item[0]:item[1]})
...
>>> dict
{'reference1': 1, 'created': datetime.datetime(2014, 2, 21, 4, 38, 51), 'value2': 2, 'value': 1, 'id': 1, 'reference2': [1]}
>>>

That should put you in a rough ballpark of the answer to your question, I hope.


回答 6

这里有很多有趣的解决方案。我的解决方案是使用dict理解将as_dict方法添加到模型中。

def as_dict(self):
    return dict((f.name, getattr(self, f.name)) for f in self._meta.fields)

另外,如果您要将模型导出到另一个库,则此解决方案与对查询的列表理解一起可以提供一个不错的解决方案。例如,将模型转储到pandas数据框中:

pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()])

Lots of interesting solutions here. My solution was to add an as_dict method to my model with a dict comprehension.

def as_dict(self):
    return dict((f.name, getattr(self, f.name)) for f in self._meta.fields)

As a bonus, this solution paired with an list comprehension over a query makes for a nice solution if you want export your models to another library. For example, dumping your models into a pandas dataframe:

pandas_awesomeness = pd.DataFrame([m.as_dict() for m in SomeModel.objects.all()])

回答 7

(并非要发表评论)

好的,它并不是真的那样依赖类型。我可能对这里的原始问题有误解,因此请原谅。如果创建serliazers.py,则在其中创建具有元类的类。

Class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = modelName
        fields =('csv','of','fields')

然后,当您在视图类中获取数据时,您可以:

model_data - Model.objects.filter(...)
serializer = MyModelSerializer(model_data, many=True)
return Response({'data': serilaizer.data}, status=status.HTTP_200_OK)

这在很多地方都差不多,它通过JSONRenderer返回了不错的JSON。

正如我所说的,这是DjangoRestFramework的礼貌,因此值得研究。

(did not mean to make the comment)

Ok, it doesn’t really depend on types in that way. I may have mis-understood the original question here so forgive me if that is the case. If you create serliazers.py then in there you create classes that have meta classes.

Class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = modelName
        fields =('csv','of','fields')

Then when you get the data in the view class you can:

model_data - Model.objects.filter(...)
serializer = MyModelSerializer(model_data, many=True)
return Response({'data': serilaizer.data}, status=status.HTTP_200_OK)

That is pretty much what I have in a vareity of places and it returns nice JSON via the JSONRenderer.

As I said this is courtesy of the DjangoRestFramework so it’s worth looking into that.


回答 8

更简单的方法是只使用pprint,这在基本Python中

import pprint
item = MyDjangoModel.objects.get(name = 'foo')
pprint.pprint(item.__dict__, indent = 4)

这给出的输出类似于,json.dumps(..., indent = 4)但可以正确处理可能嵌入在模型实例中的怪异数据类型,例如ModelStateUUID

在Python 3.7上测试

The easier way is to just use pprint, which is in base Python

import pprint
item = MyDjangoModel.objects.get(name = 'foo')
pprint.pprint(item.__dict__, indent = 4)

This gives output that looks similar to json.dumps(..., indent = 4) but it correctly handles the weird data types that might be embedded in your model instance, such as ModelState and UUID, etc.

Tested on Python 3.7


回答 9

也许这对您有帮助。也许这并不能掩盖很多对很多的关系,但是当您要以json格式发送模型时,它非常方便。

def serial_model(modelobj):
  opts = modelobj._meta.fields
  modeldict = model_to_dict(modelobj)
  for m in opts:
    if m.is_relation:
        foreignkey = getattr(modelobj, m.name)
        if foreignkey:
            try:
                modeldict[m.name] = serial_model(foreignkey)
            except:
                pass
  return modeldict

Maybe this help you. May this not covert many to many relantionship, but es pretty handy when you want to send your model in json format.

def serial_model(modelobj):
  opts = modelobj._meta.fields
  modeldict = model_to_dict(modelobj)
  for m in opts:
    if m.is_relation:
        foreignkey = getattr(modelobj, m.name)
        if foreignkey:
            try:
                modeldict[m.name] = serial_model(foreignkey)
            except:
                pass
  return modeldict

回答 10

您见过的最佳解决方案。

将django.db.models.Model实例以及所有相关的ForeignKey,ManyToManyField和@Property函数字段转换为dict。

"""
Convert django.db.models.Model instance and all related ForeignKey, ManyToManyField and @property function fields into dict.
Usage:
    class MyDjangoModel(... PrintableModel):
        to_dict_fields = (...)
        to_dict_exclude = (...)
        ...
    a_dict = [inst.to_dict(fields=..., exclude=...) for inst in MyDjangoModel.objects.all()]
"""
import typing

import django.core.exceptions
import django.db.models
import django.forms.models


def get_decorators_dir(cls, exclude: typing.Optional[set]=None) -> set:
    """
    Ref: /programming/4930414/how-can-i-introspect-properties-and-model-fields-in-django
    :param exclude: set or None
    :param cls:
    :return: a set of decorators
    """
    default_exclude = {"pk", "objects"}
    if not exclude:
        exclude = default_exclude
    else:
        exclude = exclude.union(default_exclude)

    return set([name for name in dir(cls) if name not in exclude and isinstance(getattr(cls, name), property)])


class PrintableModel(django.db.models.Model):

    class Meta:
        abstract = True

    def __repr__(self):
        return str(self.to_dict())

    def to_dict(self, fields: typing.Optional[typing.Iterable]=None, exclude: typing.Optional[typing.Iterable]=None):
        opts = self._meta
        data = {}

        # support fields filters and excludes
        if not fields:
            fields = set()
        else:
            fields = set(fields)
        default_fields = getattr(self, "to_dict_fields", set())
        fields = fields.union(default_fields)

        if not exclude:
            exclude = set()
        else:
            exclude = set(exclude)
        default_exclude = getattr(self, "to_dict_exclude", set())
        exclude = exclude.union(default_exclude)

        # support syntax "field__childField__..."
        self_fields = set()
        child_fields = dict()
        if fields:
            for i in fields:
                splits = i.split("__")
                if len(splits) == 1:
                    self_fields.add(splits[0])
                else:
                    self_fields.add(splits[0])

                    field_name = splits[0]
                    child_fields.setdefault(field_name, set())
                    child_fields[field_name].add("__".join(splits[1:]))

        self_exclude = set()
        child_exclude = dict()
        if exclude:
            for i in exclude:
                splits = i.split("__")
                if len(splits) == 1:
                    self_exclude.add(splits[0])
                else:
                    field_name = splits[0]
                    if field_name not in child_exclude:
                        child_exclude[field_name] = set()
                    child_exclude[field_name].add("__".join(splits[1:]))

        for f in opts.concrete_fields + opts.many_to_many:
            if self_fields and f.name not in self_fields:
                continue
            if self_exclude and f.name in self_exclude:
                continue

            if isinstance(f, django.db.models.ManyToManyField):
                if self.pk is None:
                    data[f.name] = []
                else:
                    result = []
                    m2m_inst = f.value_from_object(self)
                    for obj in m2m_inst:
                        if isinstance(PrintableModel, obj) and hasattr(obj, "to_dict"):
                            d = obj.to_dict(
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name),
                            )
                        else:
                            d = django.forms.models.model_to_dict(
                                obj,
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name)
                            )
                        result.append(d)
                    data[f.name] = result
            elif isinstance(f, django.db.models.ForeignKey):
                if self.pk is None:
                    data[f.name] = []
                else:
                    data[f.name] = None
                    try:
                        foreign_inst = getattr(self, f.name)
                    except django.core.exceptions.ObjectDoesNotExist:
                        pass
                    else:
                        if isinstance(foreign_inst, PrintableModel) and hasattr(foreign_inst, "to_dict"):
                            data[f.name] = foreign_inst.to_dict(
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name)
                            )
                        elif foreign_inst is not None:
                            data[f.name] = django.forms.models.model_to_dict(
                                foreign_inst,
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name),
                            )

            elif isinstance(f, (django.db.models.DateTimeField, django.db.models.DateField)):
                v = f.value_from_object(self)
                if v is not None:
                    data[f.name] = v.isoformat()
                else:
                    data[f.name] = None
            else:
                data[f.name] = f.value_from_object(self)

        # support @property decorator functions
        decorator_names = get_decorators_dir(self.__class__)
        for name in decorator_names:
            if self_fields and name not in self_fields:
                continue
            if self_exclude and name in self_exclude:
                continue

            value = getattr(self, name)
            if isinstance(value, PrintableModel) and hasattr(value, "to_dict"):
                data[name] = value.to_dict(
                    fields=child_fields.get(name),
                    exclude=child_exclude.get(name)
                )
            elif hasattr(value, "_meta"):
                # make sure it is a instance of django.db.models.fields.Field
                data[name] = django.forms.models.model_to_dict(
                    value,
                    fields=child_fields.get(name),
                    exclude=child_exclude.get(name),
                )
            elif isinstance(value, (set, )):
                data[name] = list(value)
            else:
                data[name] = value

        return data

https://gist.github.com/shuge/f543dc2094a3183f69488df2bfb51a52

Best solution you have ever see.

Convert django.db.models.Model instance and all related ForeignKey, ManyToManyField and @Property function fields into dict.

"""
Convert django.db.models.Model instance and all related ForeignKey, ManyToManyField and @property function fields into dict.
Usage:
    class MyDjangoModel(... PrintableModel):
        to_dict_fields = (...)
        to_dict_exclude = (...)
        ...
    a_dict = [inst.to_dict(fields=..., exclude=...) for inst in MyDjangoModel.objects.all()]
"""
import typing

import django.core.exceptions
import django.db.models
import django.forms.models


def get_decorators_dir(cls, exclude: typing.Optional[set]=None) -> set:
    """
    Ref: https://stackoverflow.com/questions/4930414/how-can-i-introspect-properties-and-model-fields-in-django
    :param exclude: set or None
    :param cls:
    :return: a set of decorators
    """
    default_exclude = {"pk", "objects"}
    if not exclude:
        exclude = default_exclude
    else:
        exclude = exclude.union(default_exclude)

    return set([name for name in dir(cls) if name not in exclude and isinstance(getattr(cls, name), property)])


class PrintableModel(django.db.models.Model):

    class Meta:
        abstract = True

    def __repr__(self):
        return str(self.to_dict())

    def to_dict(self, fields: typing.Optional[typing.Iterable]=None, exclude: typing.Optional[typing.Iterable]=None):
        opts = self._meta
        data = {}

        # support fields filters and excludes
        if not fields:
            fields = set()
        else:
            fields = set(fields)
        default_fields = getattr(self, "to_dict_fields", set())
        fields = fields.union(default_fields)

        if not exclude:
            exclude = set()
        else:
            exclude = set(exclude)
        default_exclude = getattr(self, "to_dict_exclude", set())
        exclude = exclude.union(default_exclude)

        # support syntax "field__childField__..."
        self_fields = set()
        child_fields = dict()
        if fields:
            for i in fields:
                splits = i.split("__")
                if len(splits) == 1:
                    self_fields.add(splits[0])
                else:
                    self_fields.add(splits[0])

                    field_name = splits[0]
                    child_fields.setdefault(field_name, set())
                    child_fields[field_name].add("__".join(splits[1:]))

        self_exclude = set()
        child_exclude = dict()
        if exclude:
            for i in exclude:
                splits = i.split("__")
                if len(splits) == 1:
                    self_exclude.add(splits[0])
                else:
                    field_name = splits[0]
                    if field_name not in child_exclude:
                        child_exclude[field_name] = set()
                    child_exclude[field_name].add("__".join(splits[1:]))

        for f in opts.concrete_fields + opts.many_to_many:
            if self_fields and f.name not in self_fields:
                continue
            if self_exclude and f.name in self_exclude:
                continue

            if isinstance(f, django.db.models.ManyToManyField):
                if self.pk is None:
                    data[f.name] = []
                else:
                    result = []
                    m2m_inst = f.value_from_object(self)
                    for obj in m2m_inst:
                        if isinstance(PrintableModel, obj) and hasattr(obj, "to_dict"):
                            d = obj.to_dict(
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name),
                            )
                        else:
                            d = django.forms.models.model_to_dict(
                                obj,
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name)
                            )
                        result.append(d)
                    data[f.name] = result
            elif isinstance(f, django.db.models.ForeignKey):
                if self.pk is None:
                    data[f.name] = []
                else:
                    data[f.name] = None
                    try:
                        foreign_inst = getattr(self, f.name)
                    except django.core.exceptions.ObjectDoesNotExist:
                        pass
                    else:
                        if isinstance(foreign_inst, PrintableModel) and hasattr(foreign_inst, "to_dict"):
                            data[f.name] = foreign_inst.to_dict(
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name)
                            )
                        elif foreign_inst is not None:
                            data[f.name] = django.forms.models.model_to_dict(
                                foreign_inst,
                                fields=child_fields.get(f.name),
                                exclude=child_exclude.get(f.name),
                            )

            elif isinstance(f, (django.db.models.DateTimeField, django.db.models.DateField)):
                v = f.value_from_object(self)
                if v is not None:
                    data[f.name] = v.isoformat()
                else:
                    data[f.name] = None
            else:
                data[f.name] = f.value_from_object(self)

        # support @property decorator functions
        decorator_names = get_decorators_dir(self.__class__)
        for name in decorator_names:
            if self_fields and name not in self_fields:
                continue
            if self_exclude and name in self_exclude:
                continue

            value = getattr(self, name)
            if isinstance(value, PrintableModel) and hasattr(value, "to_dict"):
                data[name] = value.to_dict(
                    fields=child_fields.get(name),
                    exclude=child_exclude.get(name)
                )
            elif hasattr(value, "_meta"):
                # make sure it is a instance of django.db.models.fields.Field
                data[name] = django.forms.models.model_to_dict(
                    value,
                    fields=child_fields.get(name),
                    exclude=child_exclude.get(name),
                )
            elif isinstance(value, (set, )):
                data[name] = list(value)
            else:
                data[name] = value

        return data

https://gist.github.com/shuge/f543dc2094a3183f69488df2bfb51a52


回答 11

@zags的回答很全面,应该足够了,但是#5方法(这是IMO最好的方法)抛出错误,因此我改进了辅助函数。

由于OP请求转换many_to_many领域成主键,而不是对象的列表清单,我增强了功能,所以返回值现在为JSON序列化-通过将datetime物体进入strmany_to_many对象ID的列表。

import datetime

def ModelToDict(instance):
    '''
    Returns a dictionary object containing complete field-value pairs of the given instance

    Convertion rules:

        datetime.date --> str
        many_to_many --> list of id's

    '''

    concrete_fields = instance._meta.concrete_fields
    m2m_fields = instance._meta.many_to_many
    data = {}

    for field in concrete_fields:
        key = field.name
        value = field.value_from_object(instance)
        if type(value) == datetime.datetime:
            value = str(field.value_from_object(instance))
        data[key] = value

    for field in m2m_fields:
        key = field.name
        value = field.value_from_object(instance)
        data[key] = [rel.id for rel in value]

    return data

The answer from @zags is comprehensive and should suffice but the #5 method (which is the best one IMO) throws an error so I improved the helper function.

As the OP requested for converting many_to_many fields into a list of primary keys rather than a list of objects, I enhanced the function so the return value is now JSON serializable – by converting datetime objects into str and many_to_many objects to a list of id’s.

import datetime

def ModelToDict(instance):
    '''
    Returns a dictionary object containing complete field-value pairs of the given instance

    Convertion rules:

        datetime.date --> str
        many_to_many --> list of id's

    '''

    concrete_fields = instance._meta.concrete_fields
    m2m_fields = instance._meta.many_to_many
    data = {}

    for field in concrete_fields:
        key = field.name
        value = field.value_from_object(instance)
        if type(value) == datetime.datetime:
            value = str(field.value_from_object(instance))
        data[key] = value

    for field in m2m_fields:
        key = field.name
        value = field.value_from_object(instance)
        data[key] = [rel.id for rel in value]

    return data

Django ModelAdmin中的“ list_display”可以显示ForeignKey字段的属性吗?

问题:Django ModelAdmin中的“ list_display”可以显示ForeignKey字段的属性吗?

我有一个Person模型,它与有一个外键关系Book,该模型有许多字段,但我最关心的是author(标准CharField)。

话虽如此,在我的PersonAdmin模型中,我想book.author使用显示list_display

class PersonAdmin(admin.ModelAdmin):
    list_display = ['book.author',]

我已经尝试了所有显而易见的方法来执行此操作,但是似乎没有任何效果。

有什么建议么?

I have a Person model that has a foreign key relationship to Book, which has a number of fields, but I’m most concerned about author (a standard CharField).

With that being said, in my PersonAdmin model, I’d like to display book.author using list_display:

class PersonAdmin(admin.ModelAdmin):
    list_display = ['book.author',]

I’ve tried all of the obvious methods for doing so, but nothing seems to work.

Any suggestions?


回答 0

作为另一种选择,您可以进行如下查找:

class UserAdmin(admin.ModelAdmin):
    list_display = (..., 'get_author')

    def get_author(self, obj):
        return obj.book.author
    get_author.short_description = 'Author'
    get_author.admin_order_field = 'book__author'

As another option, you can do look ups like:

class UserAdmin(admin.ModelAdmin):
    list_display = (..., 'get_author')

    def get_author(self, obj):
        return obj.book.author
    get_author.short_description = 'Author'
    get_author.admin_order_field = 'book__author'

回答 1

尽管上面有很多很棒的答案,但由于我是Django的新手,所以我仍然受困。这是我从一个新手的角度进行的解释。

models.py

class Author(models.Model):
    name = models.CharField(max_length=255)

class Book(models.Model):
    author = models.ForeignKey(Author)
    title = models.CharField(max_length=255)

admin.py(不正确的方式) -您认为使用’model__field’进行引用可以正常工作,但它不起作用

class BookAdmin(admin.ModelAdmin):
    model = Book
    list_display = ['title', 'author__name', ]

admin.site.register(Book, BookAdmin)

admin.py(正确的方式) -这就是您以Django方式引用外键名称的方式

class BookAdmin(admin.ModelAdmin):
    model = Book
    list_display = ['title', 'get_name', ]

    def get_name(self, obj):
        return obj.author.name
    get_name.admin_order_field  = 'author'  #Allows column order sorting
    get_name.short_description = 'Author Name'  #Renames column head

    #Filtering on side - for some reason, this works
    #list_filter = ['title', 'author__name']

admin.site.register(Book, BookAdmin)

有关其他参考,请参见此处的Django模型链接

Despite all the great answers above and due to me being new to Django, I was still stuck. Here’s my explanation from a very newbie perspective.

models.py

class Author(models.Model):
    name = models.CharField(max_length=255)

class Book(models.Model):
    author = models.ForeignKey(Author)
    title = models.CharField(max_length=255)

admin.py (Incorrect Way) – you think it would work by using ‘model__field’ to reference, but it doesn’t

class BookAdmin(admin.ModelAdmin):
    model = Book
    list_display = ['title', 'author__name', ]

admin.site.register(Book, BookAdmin)

admin.py (Correct Way) – this is how you reference a foreign key name the Django way

class BookAdmin(admin.ModelAdmin):
    model = Book
    list_display = ['title', 'get_name', ]

    def get_name(self, obj):
        return obj.author.name
    get_name.admin_order_field  = 'author'  #Allows column order sorting
    get_name.short_description = 'Author Name'  #Renames column head

    #Filtering on side - for some reason, this works
    #list_filter = ['title', 'author__name']

admin.site.register(Book, BookAdmin)

For additional reference, see the Django model link here


回答 2

和其余的一样,我也使用可调用对象。但是它们有一个缺点:默认情况下,您无法订购它们。幸运的是,有一个解决方案:

的Django> = 1.8

def author(self, obj):
    return obj.book.author
author.admin_order_field  = 'book__author'

Django <1.8

def author(self):
    return self.book.author
author.admin_order_field  = 'book__author'

Like the rest, I went with callables too. But they have one downside: by default, you can’t order on them. Fortunately, there is a solution for that:

Django >= 1.8

def author(self, obj):
    return obj.book.author
author.admin_order_field  = 'book__author'

Django < 1.8

def author(self):
    return self.book.author
author.admin_order_field  = 'book__author'

回答 3

请注意,添加该get_author功能会减慢admin中的list_display速度,因为显示每个人都会进行SQL查询。

为了避免这种情况,您需要get_queryset在PersonAdmin中修改方法,例如:

def get_queryset(self, request):
    return super(PersonAdmin,self).get_queryset(request).select_related('book')

之前:36.02毫秒内有73个查询(管理员中有67个重复查询)

之后:10.81毫秒内进行6次查询

Please note that adding the get_author function would slow the list_display in the admin, because showing each person would make a SQL query.

To avoid this, you need to modify get_queryset method in PersonAdmin, for example:

def get_queryset(self, request):
    return super(PersonAdmin,self).get_queryset(request).select_related('book')

Before: 73 queries in 36.02ms (67 duplicated queries in admin)

After: 6 queries in 10.81ms


回答 4

根据文档,您只能显示外键的__unicode__表示形式:

http://docs.djangoproject.com/en/dev/ref/contrib/admin/#list-display

似乎很奇怪,它不支持'book__author'DB API其他地方使用的样式格式。

原来有一张用于此功能的票证,标记为“无法修复”。

According to the documentation, you can only display the __unicode__ representation of a ForeignKey:

http://docs.djangoproject.com/en/dev/ref/contrib/admin/#list-display

Seems odd that it doesn’t support the 'book__author' style format which is used everywhere else in the DB API.

Turns out there’s a ticket for this feature, which is marked as Won’t Fix.


回答 5

我刚刚发布了一个片段,使admin.ModelAdmin支持’__’语法:

http://djangosnippets.org/snippets/2887/

因此,您可以执行以下操作:

class PersonAdmin(RelatedFieldAdmin):
    list_display = ['book__author',]

基本上,这只是在做其他答案中描述的相同操作,但是它会自动处理(1)设置admin_order_field(2)设置short_description以及(3)修改查询集以避免每一行都有数据库命中。

I just posted a snippet that makes admin.ModelAdmin support ‘__’ syntax:

http://djangosnippets.org/snippets/2887/

So you can do:

class PersonAdmin(RelatedFieldAdmin):
    list_display = ['book__author',]

This is basically just doing the same thing described in the other answers, but it automatically takes care of (1) setting admin_order_field (2) setting short_description and (3) modifying the queryset to avoid a database hit for each row.


回答 6

您可以使用可调用对象在列表显示中显示所需的任何内容。它看起来像这样:

def book_author(object):
  返回object.book.author

类PersonAdmin(admin.ModelAdmin):
  list_display = [book_author,]

You can show whatever you want in list display by using a callable. It would look like this:


def book_author(object):
  return object.book.author

class PersonAdmin(admin.ModelAdmin):
  list_display = [book_author,]

回答 7

这个已经被接受了,但是如果还有其他假人(像我一样)没有立即从当前接受的答案中得到答案,那么这里有更多细节。

ForeignKey需要引用的模型类在其中需要一个__unicode__方法,例如:

class Category(models.Model):
    name = models.CharField(max_length=50)

    def __unicode__(self):
        return self.name

这对我来说很重要,应该适用于上述情况。这适用于Django 1.0.2。

This one’s already accepted, but if there are any other dummies out there (like me) that didn’t immediately get it from the presently accepted answer, here’s a bit more detail.

The model class referenced by the ForeignKey needs to have a __unicode__ method within it, like here:

class Category(models.Model):
    name = models.CharField(max_length=50)

    def __unicode__(self):
        return self.name

That made the difference for me, and should apply to the above scenario. This works on Django 1.0.2.


回答 8

如果您有很多关联属性字段供使用,list_display并且不想为每个函数创建一个函数(及其属性),那么一个肮脏而简单的解决方案将覆盖ModelAdmininstace __getattr__方法,即刻创建可调用对象:

class DynamicLookupMixin(object):
    '''
    a mixin to add dynamic callable attributes like 'book__author' which
    return a function that return the instance.book.author value
    '''

    def __getattr__(self, attr):
        if ('__' in attr
            and not attr.startswith('_')
            and not attr.endswith('_boolean')
            and not attr.endswith('_short_description')):

            def dyn_lookup(instance):
                # traverse all __ lookups
                return reduce(lambda parent, child: getattr(parent, child),
                              attr.split('__'),
                              instance)

            # get admin_order_field, boolean and short_description
            dyn_lookup.admin_order_field = attr
            dyn_lookup.boolean = getattr(self, '{}_boolean'.format(attr), False)
            dyn_lookup.short_description = getattr(
                self, '{}_short_description'.format(attr),
                attr.replace('_', ' ').capitalize())

            return dyn_lookup

        # not dynamic lookup, default behaviour
        return self.__getattribute__(attr)


# use examples    

@admin.register(models.Person)
class PersonAdmin(admin.ModelAdmin, DynamicLookupMixin):
    list_display = ['book__author', 'book__publisher__name',
                    'book__publisher__country']

    # custom short description
    book__publisher__country_short_description = 'Publisher Country'


@admin.register(models.Product)
class ProductAdmin(admin.ModelAdmin, DynamicLookupMixin):
    list_display = ('name', 'category__is_new')

    # to show as boolean field
    category__is_new_boolean = True

作为要点

可调用的特殊属性,例如booleanshort_description必须定义为ModelAdmin属性,例如book__author_verbose_name = 'Author name'category__is_new_boolean = True

callable admin_order_field属性是自动定义的。

不要忘记在您的列表中使用list_select_related属性,ModelAdmin以使Django避免常规查询。

If you have a lot of relation attribute fields to use in list_display and do not want create a function (and it’s attributes) for each one, a dirt but simple solution would be override the ModelAdmin instace __getattr__ method, creating the callables on the fly:

class DynamicLookupMixin(object):
    '''
    a mixin to add dynamic callable attributes like 'book__author' which
    return a function that return the instance.book.author value
    '''

    def __getattr__(self, attr):
        if ('__' in attr
            and not attr.startswith('_')
            and not attr.endswith('_boolean')
            and not attr.endswith('_short_description')):

            def dyn_lookup(instance):
                # traverse all __ lookups
                return reduce(lambda parent, child: getattr(parent, child),
                              attr.split('__'),
                              instance)

            # get admin_order_field, boolean and short_description
            dyn_lookup.admin_order_field = attr
            dyn_lookup.boolean = getattr(self, '{}_boolean'.format(attr), False)
            dyn_lookup.short_description = getattr(
                self, '{}_short_description'.format(attr),
                attr.replace('_', ' ').capitalize())

            return dyn_lookup

        # not dynamic lookup, default behaviour
        return self.__getattribute__(attr)


# use examples    

@admin.register(models.Person)
class PersonAdmin(admin.ModelAdmin, DynamicLookupMixin):
    list_display = ['book__author', 'book__publisher__name',
                    'book__publisher__country']

    # custom short description
    book__publisher__country_short_description = 'Publisher Country'


@admin.register(models.Product)
class ProductAdmin(admin.ModelAdmin, DynamicLookupMixin):
    list_display = ('name', 'category__is_new')

    # to show as boolean field
    category__is_new_boolean = True

As gist here

Callable especial attributes like boolean and short_description must be defined as ModelAdmin attributes, eg book__author_verbose_name = 'Author name' and category__is_new_boolean = True.

The callable admin_order_field attribute is defined automatically.

Don’t forget to use the list_select_related attribute in your ModelAdmin to make Django avoid aditional queries.


回答 9

PyPI中有一个非常易于使用的软件包,可以准确地处理该软件包:django-related-admin。您还可以在GitHub中查看代码

使用此功能,您想要实现的过程很简单:

class PersonAdmin(RelatedFieldAdmin):
    list_display = ['book__author',]

这两个链接均包含安装和使用的完整详细信息,因此,如果它们发生更改,我就不会在此处粘贴它们。

顺便提一句,如果您已经使用了其他工具model.Admin(例如,我正在使用SimpleHistoryAdmin),则可以执行以下操作:class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin)

There is a very easy to use package available in PyPI that handles exactly that: django-related-admin. You can also see the code in GitHub.

Using this, what you want to achieve is as simple as:

class PersonAdmin(RelatedFieldAdmin):
    list_display = ['book__author',]

Both links contain full details of installation and usage so I won’t paste them here in case they change.

Just as a side note, if you’re already using something other than model.Admin (e.g. I was using SimpleHistoryAdmin instead), you can do this: class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin).


回答 10

如果您在Inline中尝试,除非以下条件,否则您不会成功:

在您的内联中:

class AddInline(admin.TabularInline):
    readonly_fields = ['localname',]
    model = MyModel
    fields = ('localname',)

在您的模型(MyModel)中:

class MyModel(models.Model):
    localization = models.ForeignKey(Localizations)

    def localname(self):
        return self.localization.name

if you try it in Inline, you wont succeed unless:

in your inline:

class AddInline(admin.TabularInline):
    readonly_fields = ['localname',]
    model = MyModel
    fields = ('localname',)

in your model (MyModel):

class MyModel(models.Model):
    localization = models.ForeignKey(Localizations)

    def localname(self):
        return self.localization.name

回答 11

AlexRobbins的答案对我有用,除了前两行需要在模型中(也许这是假设的?),并且应该引用self:

def book_author(self):
  return self.book.author

然后管理部分可以很好地工作。

AlexRobbins’ answer worked for me, except that the first two lines need to be in the model (perhaps this was assumed?), and should reference self:

def book_author(self):
  return self.book.author

Then the admin part works nicely.


回答 12

我更喜欢这样:

class CoolAdmin(admin.ModelAdmin):
    list_display = ('pk', 'submodel__field')

    @staticmethod
    def submodel__field(obj):
        return obj.submodel.field

I prefer this:

class CoolAdmin(admin.ModelAdmin):
    list_display = ('pk', 'submodel__field')

    @staticmethod
    def submodel__field(obj):
        return obj.submodel.field

Django ORM中的select_related和prefetch_related有什么区别?

问题:Django ORM中的select_related和prefetch_related有什么区别?

在Django文件中,

select_related() “遵循”外键关系,在执行查询时选择其他相关对象数据。

prefetch_related() 对每个关系进行单独的查找,并在Python中执行“联接”。

“在python中进行连接”是什么意思?有人可以举例说明吗?

我的理解是,对于外键关系,使用select_related; 对于M2M关系,请使用prefetch_related。它是否正确?

In Django doc,

select_related() “follows” foreign-key relationships, selecting additional related-object data when it executes its query.

prefetch_related() does a separate lookup for each relationship, and does the “joining” in Python.

What does it mean by “doing the joining in python”? Can someone illustrate with an example?

My understanding is that for foreign key relationship, use select_related; and for M2M relationship, use prefetch_related. Is this correct?


回答 0

您的理解基本上是正确的。您可以使用select_related时,你将要选择的对象是一个对象,所以OneToOneField还是ForeignKey。您可以使用prefetch_related时,你会得到一个东西的“设置”,所以ManyToManyFieldS作为你陈述或反向ForeignKey秒。为了阐明我的意思是“ reverse ForeignKeys”,这里有一个例子:

class ModelA(models.Model):
    pass

class ModelB(models.Model):
    a = ForeignKey(ModelA)

ModelB.objects.select_related('a').all() # Forward ForeignKey relationship
ModelA.objects.prefetch_related('modelb_set').all() # Reverse ForeignKey relationship

区别在于select_related执行SQL连接,因此从SQL Server将结果作为表的一部分返回。prefetch_related另一方面,执行另一个查询,因此减少了原始对象中的冗余列(ModelA在上面的示例中)。您可以使用prefetch_related任何可以使用的东西select_related

折衷方案是prefetch_related必须创建并发送ID列表以选择回服务器,这可能需要一段时间。我不确定在事务中是否有很好的方法,但是我的理解是Django总是只发送一个列表并显示SELECT … WHERE PK IN(…,…,…)基本上。在这种情况下,如果预取的数据稀疏(例如,将美国国家对象链接到人们的地址),这可能会很好,但是,如果它们之间的关系更接近一对一,则会浪费大量通信资源。如有疑问,请尝试两者并查看哪种效果更好。

上面讨论的所有内容基本上都与与数据库的通信有关。但是,在Python方面prefetch_related具有额外的好处,即使用单个对象表示数据库中的每个对象。使用select_related重复的对象将在Python中为每个“父”对象创建。由于Python中的对象具有相当大的内存开销,因此这也是一个考虑因素。

Your understanding is mostly correct. You use select_related when the object that you’re going to be selecting is a single object, so OneToOneField or a ForeignKey. You use prefetch_related when you’re going to get a “set” of things, so ManyToManyFields as you stated or reverse ForeignKeys. Just to clarify what I mean by “reverse ForeignKeys” here’s an example:

class ModelA(models.Model):
    pass

class ModelB(models.Model):
    a = ForeignKey(ModelA)

ModelB.objects.select_related('a').all() # Forward ForeignKey relationship
ModelA.objects.prefetch_related('modelb_set').all() # Reverse ForeignKey relationship

The difference is that select_related does an SQL join and therefore gets the results back as part of the table from the SQL server. prefetch_related on the other hand executes another query and therefore reduces the redundant columns in the original object (ModelA in the above example). You may use prefetch_related for anything that you can use select_related for.

The tradeoffs are that prefetch_related has to create and send a list of IDs to select back to the server, this can take a while. I’m not sure if there’s a nice way of doing this in a transaction, but my understanding is that Django always just sends a list and says SELECT … WHERE pk IN (…,…,…) basically. In this case if the prefetched data is sparse (let’s say U.S. State objects linked to people’s addresses) this can be very good, however if it’s closer to one-to-one, this can waste a lot of communications. If in doubt, try both and see which performs better.

Everything discussed above is basically about the communications with the database. On the Python side however prefetch_related has the extra benefit that a single object is used to represent each object in the database. With select_related duplicate objects will be created in Python for each “parent” object. Since objects in Python have a decent bit of memory overhead this can also be a consideration.


回答 1

两种方法可以达到相同的目的,从而放弃不必要的数据库查询。但是他们使用不同的方法来提高效率。

使用这两种方法的唯一原因是,当单个大型查询优于许多小型查询时。Django使用大型查询来抢先在内存中创建模型,而不是针对数据库执行按需查询。

select_related对每个查找执行联接,但将选择范围扩展为包括所有联接表的列。但是,这种方法有一个警告。

联接有可能使查询中的行数相乘。当您通过外键或一对一字段执行联接时,行数不会增加。但是,多对多联接没有此保证。因此,Django限制select_related了不会意外导致大规模联接的关系。

对于“ join in python”来说prefetch_related,应该比它还要令人震惊。它为要连接的每个表创建一个单独的查询。它使用WHERE IN子句过滤每个表,例如:

SELECT "credential"."id",
       "credential"."uuid",
       "credential"."identity_id"
FROM   "credential"
WHERE  "credential"."identity_id" IN
    (84706, 48746, 871441, 84713, 76492, 84621, 51472);

每个表都被拆分成一个单独的查询,而不是执行可能包含太多行的单个联接。

Both methods achieve the same purpose, to forego unnecessary db queries. But they use different approaches for efficiency.

The only reason to use either of these methods is when a single large query is preferable to many small queries. Django uses the large query to create models in memory preemptively rather than performing on demand queries against the database.

select_related performs a join with each lookup, but extends the select to include the columns of all joined tables. However this approach has a caveat.

Joins have the potential to multiply the number of rows in a query. When you perform a join over a foreign key or one-to-one field, the number of rows won’t increase. However, many-to-many joins do not have this guarantee. So, Django restricts select_related to relations that won’t unexpectedly result in a massive join.

The “join in python” for prefetch_related is a little more alarming then it should be. It creates a separate query for each table to be joined. It filters each of these table with a WHERE IN clause, like:

SELECT "credential"."id",
       "credential"."uuid",
       "credential"."identity_id"
FROM   "credential"
WHERE  "credential"."identity_id" IN
    (84706, 48746, 871441, 84713, 76492, 84621, 51472);

Rather than performing a single join with potentially too many rows, each table is split into a separate query.


回答 2

如Django文档所述:

prefetch_related()

返回一个QuerySet,该查询集将自动为每个指定的查询分批检索相关对象。

这与select_related具有相似的目的,因为两者均旨在阻止由于访问相关对象而导致的数据库查询泛滥,但是策略却大不相同。

select_related通过创建SQL连接并将相关对象的字段包括在SELECT语句中来工作。因此,select_related在同一数据库查询中获取相关对象。但是,为了避免跨“许多”关系进行联接会产生更大的结果集,select_related仅限于单值关系-外键和一对一关系。

另一方面,prefetch_related对每个关系进行单独的查找,并在Python中进行“联接”。除了select_related支持的外键和一对一关系之外,这还允许它预取多对多和多对一对象,这不能使用select_related完成。它还支持GenericRelation和GenericForeignKey的预取,但是,必须将其限制为同类结果。例如,仅当查询仅限于一个ContentType时,才支持预取GenericForeignKey引用的对象。

有关此的更多信息:https : //docs.djangoproject.com/en/2.2/ref/models/querysets/#prefetch-related

As Django documentation says:

prefetch_related()

Returns a QuerySet that will automatically retrieve, in a single batch, related objects for each of the specified lookups.

This has a similar purpose to select_related, in that both are designed to stop the deluge of database queries that is caused by accessing related objects, but the strategy is quite different.

select_related works by creating an SQL join and including the fields of the related object in the SELECT statement. For this reason, select_related gets the related objects in the same database query. However, to avoid the much larger result set that would result from joining across a ‘many’ relationship, select_related is limited to single-valued relationships – foreign key and one-to-one.

prefetch_related, on the other hand, does a separate lookup for each relationship, and does the ‘joining’ in Python. This allows it to prefetch many-to-many and many-to-one objects, which cannot be done using select_related, in addition to the foreign key and one-to-one relationships that are supported by select_related. It also supports prefetching of GenericRelation and GenericForeignKey, however, it must be restricted to a homogeneous set of results. For example, prefetching objects referenced by a GenericForeignKey is only supported if the query is restricted to one ContentType.

More information about this: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#prefetch-related


回答 3

仔细阅读已经发布的答案。只是认为如果我添加一个带有实际示例的答案会更好。

假设您有3个相关的Django模型。

class M1(models.Model):
    name = models.CharField(max_length=10)

class M2(models.Model):
    name = models.CharField(max_length=10)
    select_relation = models.ForeignKey(M1, on_delete=models.CASCADE)
    prefetch_relation = models.ManyToManyField(to='M3')

class M3(models.Model):
    name = models.CharField(max_length=10)

在这里,您可以使用字段和使用字段的对象查询M2模型及其相关M1对象。select_relationM3prefetch_relation

但是正如我们所提到M1的关系由M2ForeignKey,它只返回只有1对任何记录M2对象。同样的事情也适用OneToOneField

但是M3与的关系来自M2ManyToManyField它可能返回任意数量的M1对象。

考虑这样一种情况:您有2个M2对象m21m22这些对象具有相同的5个M3具有ID的关联对象1,2,3,4,5。当您M3为每个对象获取关联的M2对象时,如果使用select related,则它将如何工作。

脚步:

  1. 查找m21对象。
  2. 查询M3m21ID为的对象相关的所有对象1,2,3,4,5
  3. m22对象和所有其他M2对象重复相同的操作。

因为我们有相同1,2,3,4,5的ID两个m21m22对象,如果我们使用select_related选项,它会查询数据库两次,这已经获取相同的ID。

相反,如果您使用prefetch_related,则当您尝试获取M2对象时,它将在查询M2表时记下对象返回的所有ID(注意:仅这些ID),并且作为最后一步,Django将对M3表进行查询以及您的M2对象已返回的所有ID的集合。并M2使用Python而不是数据库将它们连接到对象。

这样,您M3只查询一次所有对象,从而提高了性能。

Gone through the already posted answers. Just thought it would be better if I add an answer with actual example.

Let’ say you have 3 Django models which are related.

class M1(models.Model):
    name = models.CharField(max_length=10)

class M2(models.Model):
    name = models.CharField(max_length=10)
    select_relation = models.ForeignKey(M1, on_delete=models.CASCADE)
    prefetch_relation = models.ManyToManyField(to='M3')

class M3(models.Model):
    name = models.CharField(max_length=10)

Here you can query M2 model and its relative M1 objects using select_relation field and M3 objects using prefetch_relation field.

However as we’ve mentioned M1‘s relation from M2 is a ForeignKey, it just returns only 1 record for any M2 object. Same thing applies for OneToOneField as well.

But M3‘s relation from M2 is a ManyToManyField which might return any number of M1 objects.

Consider a case where you have 2 M2 objects m21, m22 who have same 5 associated M3 objects with IDs 1,2,3,4,5. When you fetch associated M3 objects for each of those M2 objects, if you use select related, this is how it’s going to work.

Steps:

  1. Find m21 object.
  2. Query all the M3 objects related to m21 object whose IDs are 1,2,3,4,5.
  3. Repeat same thing for m22 object and all other M2 objects.

As we have same 1,2,3,4,5 IDs for both m21, m22 objects, if we use select_related option, it’s going to query the DB twice for the same IDs which were already fetched.

Instead if you use prefetch_related, when you try to get M2 objects, it will make a note of all the IDs that your objects returned (Note: only the IDs) while querying M2 table and as last step, Django is going to make a query to M3 table with the set of all IDs that your M2 objects have returned. and join them to M2 objects using Python instead of database.

This way you’re querying all the M3 objects only once which improves performance.


如何将两个“唯一”字段定义为一对

问题:如何将两个“唯一”字段定义为一对

有没有一种方法可以将几个字段定义为Django中唯一的字段?

我有一张(期刊的)卷表,而我不希望同一期刊有一个以上的卷号。

class Volume(models.Model):
    id = models.AutoField(primary_key=True)
    journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name = "Journal")
    volume_number = models.CharField('Volume Number', max_length=100)
    comments = models.TextField('Comments', max_length=4000, blank=True)

我试图将unique = Trueas属性放在字段中journal_idvolume_number但是不起作用。

Is there a way to define a couple of fields as unique in Django?

I have a table of volumes (of journals) and I don’t want more then one volume number for the same journal.

class Volume(models.Model):
    id = models.AutoField(primary_key=True)
    journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name = "Journal")
    volume_number = models.CharField('Volume Number', max_length=100)
    comments = models.TextField('Comments', max_length=4000, blank=True)

I tried to put unique = True as attribute in the fields journal_id and volume_number but it doesn’t work.


回答 0

有一个简单的解决方案称为unique_together,它可以完全满足您的要求。

例如:

class MyModel(models.Model):
  field1 = models.CharField(max_length=50)
  field2 = models.CharField(max_length=50)

  class Meta:
    unique_together = ('field1', 'field2',)

在您的情况下:

class Volume(models.Model):
  id = models.AutoField(primary_key=True)
  journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name = "Journal")
  volume_number = models.CharField('Volume Number', max_length=100)
  comments = models.TextField('Comments', max_length=4000, blank=True)

  class Meta:
    unique_together = ('journal_id', 'volume_number',)

There is a simple solution for you called unique_together which does exactly what you want.

For example:

class MyModel(models.Model):
  field1 = models.CharField(max_length=50)
  field2 = models.CharField(max_length=50)

  class Meta:
    unique_together = ('field1', 'field2',)

And in your case:

class Volume(models.Model):
  id = models.AutoField(primary_key=True)
  journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name = "Journal")
  volume_number = models.CharField('Volume Number', max_length=100)
  comments = models.TextField('Comments', max_length=4000, blank=True)

  class Meta:
    unique_together = ('journal_id', 'volume_number',)

回答 1

Django 2.2以上

使用constraints功能UniqueConstraint优于unique_together

从Django文档中获得unique_together

改用UniqueConstraint和Constraints选项。
UniqueConstraint提供的功能比unique_together还要多。
将来可能不推荐使用unique_together。

例如:

class Volume(models.Model):
    id = models.AutoField(primary_key=True)
    journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name="Journal")
    volume_number = models.CharField('Volume Number', max_length=100)
    comments = models.TextField('Comments', max_length=4000, blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['journal_id', 'volume_number'], name='name of constraint')
        ]

Django 2.2+

Using the constraints features UniqueConstraint is preferred over unique_together.

From the Django documentation for unique_together:

Use UniqueConstraint with the constraints option instead.
UniqueConstraint provides more functionality than unique_together.
unique_together may be deprecated in the future.

For example:

class Volume(models.Model):
    id = models.AutoField(primary_key=True)
    journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name="Journal")
    volume_number = models.CharField('Volume Number', max_length=100)
    comments = models.TextField('Comments', max_length=4000, blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['journal_id', 'volume_number'], name='name of constraint')
        ]

on_delete对Django模型有什么作用?

问题:on_delete对Django模型有什么作用?

我对Django非常熟悉,但是最近发现on_delete=models.CASCADE模型中存在一个选项,我在文档中搜索了相同的选项,但找不到以下内容:

在Django 1.9中进行了更改:

on_delete现在可以用作第二个位置参数(以前通常只作为关键字参数传递)。在Django 2.0中,这是必填参数。

使用的一个例子是

from django.db import models

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'Manufacturer',
        on_delete=models.CASCADE,
    )
    # ...

class Manufacturer(models.Model):
    # ...
    pass

on_delete是做什么的?(我想如果删除模型,要执行的操作

怎么models.CASCADE办?(文档中的任何提示

还有其他可用的选项(如果我的猜测是正确的)?

有关此文档的位置在哪里?

I’m quite familiar with Django, but recently noticed there exists an on_delete=models.CASCADE option with the models, I have searched for the documentation for the same but couldn’t find anything more than:

Changed in Django 1.9:

on_delete can now be used as the second positional argument (previously it was typically only passed as a keyword argument). It will be a required argument in Django 2.0.

an example case of usage is

from django.db import models

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'Manufacturer',
        on_delete=models.CASCADE,
    )
    # ...

class Manufacturer(models.Model):
    # ...
    pass

What does on_delete do? (I guess the actions to be done if the model is deleted)

What does models.CASCADE do? (any hints in documentation)

What other options are available (if my guess is correct)?

Where does the documentation for this reside?


回答 0

这是删除引用对象时采取的行为。它不是特定于Django的,这是一种SQL标准。

发生此类事件时,有6种可能的操作:

  • CASCADE:删除引用的对象时,还请删除引用了该对象的对象(例如,删除博客文章时,您可能还希望删除注释)。SQL等效项:CASCADE
  • PROTECT:禁止删除引用的对象。要删除它,您将必须删除所有手动引用它的对象。SQL等效项:RESTRICT
  • SET_NULL:将引用设置为NULL(要求该字段可为空)。例如,当删除用户时,您可能希望保留他在博客文章中发布的评论,但说该评论是由匿名(或已删除)用户发布的。SQL等效项:SET NULL
  • SET_DEFAULT:设置默认值。SQL等效项:SET DEFAULT
  • SET(...):设置给定值。这不是SQL标准的一部分,完全由Django处理。
  • DO_NOTHING:这可能是一个非常糟糕的主意,因为这会在数据库中造成完整性问题(引用实际上不存在的对象)。SQL等效项:NO ACTION

资料来源:Django说明文件

例如,另请参阅PostGreSQL文档

在大多数情况下,这CASCADE是预期的行为,但是对于每个ForeignKey,您应始终问自己在这种情况下的预期行为是什么。PROTECT并且SET_NULL经常有用。设置CASCADE不应该设置的位置,可以通过简单地删除单个用户来级联删除所有数据库。


附加说明以阐明级联方向

有趣的是,注意到CASCADE行动的方向对于许多人来说并不明确。事实上,这很有趣地看到,只有CASCADE行动并不清楚。我知道级联行为可能会造成混淆,但是您必须认为它与任何其他动作是同一方向。因此,如果您觉得自己CASCADE不清楚方向,那实际上意味着on_delete您不清楚自己的行为。

在您的数据库中,外键基本上由一个整数字段表示,该字段的值是外对象的主键。假设您有一个comment_A条目,它具有一个article_B条目的外键。如果您删除条目comment_A,那么一切都很好,article_B以前可以不带有comment_A生存,并且也不会被删除。但是,如果删除article_B,则comment_A会慌!它永远都离不开article_B并需要它,它是它属性的一部分(article=article_B,但是* article_B ** ???)。这是on_delete确定如何解决此完整性错误的步骤,或者说:

  • “不!请!不要!我不能没有你!” PROTECT用SQL语言表示)
  • “好吧,如果我不是你的,那我就不是任何人的”(说SET_NULL
  • “再见,我不能没有article_B生活”自杀(这是CASCADE行为)。
  • “没关系,我有多余的恋人,从现在开始我将引用article_C”SET_DEFAULT,甚至SET(...))。
  • “我不能面对现实,即使那是我唯一的事情,我也会继续给你起名字!” DO_NOTHING

我希望它使级联方向更清晰。:)

This is the behaviour to adopt when the referenced object is deleted. It is not specific to django, this is an SQL standard.

There are 6 possible actions to take when such event occurs:

  • CASCADE: When the referenced object is deleted, also delete the objects that have references to it (When you remove a blog post for instance, you might want to delete comments as well). SQL equivalent: CASCADE.
  • PROTECT: Forbid the deletion of the referenced object. To delete it you will have to delete all objects that reference it manually. SQL equivalent: RESTRICT.
  • SET_NULL: Set the reference to NULL (requires the field to be nullable). For instance, when you delete a User, you might want to keep the comments he posted on blog posts, but say it was posted by an anonymous (or deleted) user. SQL equivalent: SET NULL.
  • SET_DEFAULT: Set the default value. SQL equivalent: SET DEFAULT.
  • SET(...): Set a given value. This one is not part of the SQL standard and is entirely handled by Django.
  • DO_NOTHING: Probably a very bad idea since this would create integrity issues in your database (referencing an object that actually doesn’t exist). SQL equivalent: NO ACTION.

Source: Django documentation

See also the documentation of PostGreSQL for instance.

In most cases, CASCADE is the expected behaviour, but for every ForeignKey, you should always ask yourself what is the expected behaviour in this situation. PROTECT and SET_NULL are often useful. Setting CASCADE where it should not, can potentially delete all your database in cascade, by simply deleting a single user.


Additional note to clarify cascade direction

It’s funny to notice that the direction of the CASCADE action is not clear to many people. Actually, it’s funny to notice that only the CASCADE action is not clear. I understand the cascade behavior might be confusing, however you must think that it is the same direction as any other action. Thus, if you feel that CASCADE direction is not clear to you, it actually means that on_delete behavior is not clear to you.

In your database, a foreign key is basically represented by an integer field which value is the primary key of the foreign object. Let’s say you have an entry comment_A, which has a foreign key to an entry article_B. If you delete the entry comment_A, everything is fine, article_B used to live without comment_A and don’t bother if it’s deleted. However, if you delete article_B, then comment_A panics! It never lived without article_B and needs it, it’s part of its attributes (article=article_B, but what is *article_B**???). This is where on_delete steps in, to determine how to resolve this integrity error, either by saying:

  • “No! Please! Don’t! I can’t live without you!” (which is said PROTECT in SQL language)
  • “Alright, if I’m not yours, then I’m nobody’s” (which is said SET_NULL)
  • “Good bye world, I can’t live without article_B” and commit suicide (this is the CASCADE behavior).
  • “It’s OK, I’ve got spare lover, I’ll reference article_C from now” (SET_DEFAULT, or even SET(...)).
  • “I can’t face reality, I’ll keep calling your name even if that’s the only thing left to me!” (DO_NOTHING)

I hope it makes cascade direction clearer. :)


回答 1

on_delete方法用于告诉Django如何处理依赖于您删除的模型实例的模型实例。(例如,ForeignKey恋爱关系)。该命令on_delete=models.CASCADE告诉Django级联删除效果,即也继续删除相关模型。

这是一个更具体的例子。假设您有一个Author模型ForeignKey中的一个Book模型。现在,如果删除Author模型实例,则Django将不知道如何处理Book依赖于该Author模型实例的模型实例。该on_delete方法告诉Django在这种情况下该怎么做。设置on_delete=models.CASCADE将指示Django级联删除效果,即删除所有Book依赖于Author您删除的模型实例的模型实例。

注意:on_delete在Django 2.0中将成为必填参数。在旧版本中,默认为CASCADE

这是完整的官方文档。

The on_delete method is used to tell Django what to do with model instances that depend on the model instance you delete. (e.g. a ForeignKey relationship). The on_delete=models.CASCADE tells Django to cascade the deleting effect i.e. continue deleting the dependent models as well.

Here’s a more concrete example. Assume you have an Author model that is a ForeignKey in a Book model. Now, if you delete an instance of the Author model, Django would not know what to do with instances of the Book model that depend on that instance of Author model. The on_delete method tells Django what to do in that case. Setting on_delete=models.CASCADE will instruct Django to cascade the deleting effect i.e. delete all the Book model instances that depend on the Author model instance you deleted.

Note: on_delete will become a required argument in Django 2.0. In older versions it defaults to CASCADE.

Here’s the entire official documentation.


回答 2

仅供参考,on_delete模型中的参数从听起来像是倒过来的。您on_delete在模型上放置了外键(FK),以告诉django如果删除了记录中指向的FK条目该怎么办。选项我们店已经使用的大多是PROTECTCASCADESET_NULL。这是我弄清楚的基本规则:

  1. 使用PROTECT时,你的FK指向一个查表真的不应该被改变,并且肯定不会引起你的表来改变。如果有人试图删除该查询表上的条目,则PROTECT防止该条目与任何记录绑定时删除该条目。它还可以防止从删除的Django 你的记录,只是因为它删除了一个查找表中的条目。最后一部分至关重要。 如果有人要从“性别”表中删除性别“女性”,我肯定不希望立即删除我在“人”表中拥有该性别的任何人。
  2. 使用CASCADE时,你的FK指向“父”的纪录。所以,如果一个人可以有很多PersonEthnicity项(他/她可以是美洲印第安人,黑色和白色),而那个人删除了,我真的想什么“孩子” PersonEthnicity条目被删除。没有人,他们是无关紧要的。
  3. 使用SET_NULL时,你希望人们被允许删除查找表中的条目,但你仍然要保留记录。例如,如果某人可以拥有一所高中,但对我而言,那所高中不在我的查询表上并不重要on_delete=SET_NULL。这会将我的“个人”记录保留在那里;只会将“我的人”上的高中FK设置为​​null。显然,您必须允许null=True该FK。

这是一个可以完成所有三件事的模型示例:

class PurchPurchaseAccount(models.Model):
    id = models.AutoField(primary_key=True)
    purchase = models.ForeignKey(PurchPurchase, null=True, db_column='purchase', blank=True, on_delete=models.CASCADE) # If "parent" rec gone, delete "child" rec!!!
    paid_from_acct = models.ForeignKey(PurchPaidFromAcct, null=True, db_column='paid_from_acct', blank=True, on_delete=models.PROTECT) # Disallow lookup deletion & do not delete this rec.
    _updated = models.DateTimeField()
    _updatedby = models.ForeignKey(Person, null=True, db_column='_updatedby', blank=True, related_name='acctupdated_by', on_delete=models.SET_NULL) # Person records shouldn't be deleted, but if they are, preserve this PurchPurchaseAccount entry, and just set this person to null.

    def __unicode__(self):
        return str(self.paid_from_acct.display)
    class Meta:
        db_table = u'purch_purchase_account'

作为最后一个提示,您是否知道如果指定on_delete(或未指定),默认行为是CASCADE?这意味着,如果有人删除了您“性别”表上的性别条目,则具有该性别的任何“人”记录也将被删除!

我会说:“如果有疑问,那就出发on_delete=models.PROTECT。” 然后测试您的应用程序。您将快速找出哪些FK应该标记为其他值,而不会危及您的任何数据。

另外,值得注意的on_delete=CASCADE是,如果这是您选择的行为,实际上并没有添加到您的任何迁移中。我猜这是因为它是默认设置,所以放置on_delete=CASCADE和放置任何东西都是一样的。

FYI, the on_delete parameter in models is backwards from what it sounds like. You put on_delete on a Foreign Key (FK) on a model to tell django what to do if the FK entry that you are pointing to on your record is deleted. The options our shop have used the most are PROTECT, CASCADE, and SET_NULL. Here are the basic rules I have figured out:

  1. Use PROTECT when your FK is pointing to a look-up table that really shouldn’t be changing and that certainly should not cause your table to change. If anyone tries to delete an entry on that look-up table, PROTECT prevents them from deleting it if it is tied to any records. It also prevents django from deleting your record just because it deleted an entry on a look-up table. This last part is critical. If someone were to delete the gender “Female” from my Gender table, I CERTAINLY would NOT want that to instantly delete any and all people I had in my Person table who had that gender.
  2. Use CASCADE when your FK is pointing to a “parent” record. So, if a Person can have many PersonEthnicity entries (he/she can be American Indian, Black, and White), and that Person is deleted, I really would want any “child” PersonEthnicity entries to be deleted. They are irrelevant without the Person.
  3. Use SET_NULL when you do want people to be allowed to delete an entry on a look-up table, but you still want to preserve your record. For example, if a Person can have a HighSchool, but it doesn’t really matter to me if that high-school goes away on my look-up table, I would say on_delete=SET_NULL. This would leave my Person record out there; it just would just set the high-school FK on my Person to null. Obviously, you will have to allow null=True on that FK.

Here is an example of a model that does all three things:

class PurchPurchaseAccount(models.Model):
    id = models.AutoField(primary_key=True)
    purchase = models.ForeignKey(PurchPurchase, null=True, db_column='purchase', blank=True, on_delete=models.CASCADE) # If "parent" rec gone, delete "child" rec!!!
    paid_from_acct = models.ForeignKey(PurchPaidFromAcct, null=True, db_column='paid_from_acct', blank=True, on_delete=models.PROTECT) # Disallow lookup deletion & do not delete this rec.
    _updated = models.DateTimeField()
    _updatedby = models.ForeignKey(Person, null=True, db_column='_updatedby', blank=True, related_name='acctupdated_by', on_delete=models.SET_NULL) # Person records shouldn't be deleted, but if they are, preserve this PurchPurchaseAccount entry, and just set this person to null.

    def __unicode__(self):
        return str(self.paid_from_acct.display)
    class Meta:
        db_table = u'purch_purchase_account'

As a last tidbit, did you know that if you don’t specify on_delete (or didn’t), the default behavior is CASCADE? This means that if someone deleted a gender entry on your Gender table, any Person records with that gender were also deleted!

I would say, “If in doubt, set on_delete=models.PROTECT.” Then go test your application. You will quickly figure out which FKs should be labeled the other values without endangering any of your data.

Also, it is worth noting that on_delete=CASCADE is actually not added to any of your migrations, if that is the behavior you are selecting. I guess this is because it is the default, so putting on_delete=CASCADE is the same thing as putting nothing.


回答 3

如前所述,CASCADE将删除具有外键的记录,并引用另一个已删除的对象。因此,例如,如果您有一个房地产网站,并且有一个引用城市的房地产

class City(models.Model):
    # define model fields for a city

class Property(models.Model):
    city = models.ForeignKey(City, on_delete = models.CASCADE)
    # define model fields for a property

现在,当从数据库中删除城市时,所有关联的属性(例如,位于该城市的房地产)也将从数据库中删除

现在,我还要提及其他选项的优点,例如SET_NULL或SET_DEFAULT甚至DO_NOTHING。基本上,从管理角度来看,您要“删除”这些记录。但是您真的不希望它们消失。因为许多的原因。可能有人不小心删除了该文件,或者进行了审核和监视。和简单的报告。因此,这可能是一种将财产与城市“断开连接”的方式。同样,这将取决于您的应用程序的编写方式。

例如,某些应用程序的“已删除”字段为0或1。所有搜索和列表视图等内容,可能出现在报表中或用户可以从前端访问它的任何位置,均不包括deleted == 1。但是,如果您创建自定义报告或自定义查询来下拉已删除记录的列表,甚至更多,以便查看上次修改的时间(另一个字段)以及由谁(即谁删除它和何时删除)。从行政角度来看,这是非常有利的。

并且不要忘记,您可以像还原deleted = 0那些记录一样简单地还原意外删除。

我的观点是,如果有功能,总会有其背后的原因。并非总是一个很好的理由。但这是一个原因。往往也是一个好人。

As mentioned earlier, CASCADE will delete the record that has a foreign key and references another object that was deleted. So for example if you have a real estate website and have a Property that references a City

class City(models.Model):
    # define model fields for a city

class Property(models.Model):
    city = models.ForeignKey(City, on_delete = models.CASCADE)
    # define model fields for a property

and now when the City is deleted from the database, all associated Properties (eg. real estate located in that city) will also be deleted from the database

Now I also want to mention the merit of other options, such as SET_NULL or SET_DEFAULT or even DO_NOTHING. Basically, from the administration perspective, you want to “delete” those records. But you don’t really want them to disappear. For many reasons. Someone might have deleted it accidentally, or for auditing and monitoring. And plain reporting. So it can be a way to “disconnect” the property from a City. Again, it will depend on how your application is written.

For example, some applications have a field “deleted” which is 0 or 1. And all their searches and list views etc, anything that can appear in reports or anywhere the user can access it from the front end, exclude anything that is deleted == 1. However, if you create a custom report or a custom query to pull down a list of records that were deleted and even more so to see when it was last modified (another field) and by whom (i.e. who deleted it and when)..that is very advantageous from the executive standpoint.

And don’t forget that you can revert accidental deletions as simple as deleted = 0 for those records.

My point is, if there is a functionality, there is always a reason behind it. Not always a good reason. But a reason. And often a good one too.


回答 4

这是您的问题答案:为什么我们使用on_delete?

删除由ForeignKey引用的对象时,默认情况下,Django会模拟SQL约束ON DELETE CASCADE的行为,并删除包含ForeignKey的对象。通过指定on_delete参数可以覆盖此行为。例如,如果您具有可为空的ForeignKey,并且希望在删除引用的对象时将其设置为null:

user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)

on_delete的可能值在django.db.models中找到:

级联级联删除;默认值。

保护:通过引发ProtectedError(django.db.IntegrityError的子类)来防止删除引用的对象。

SET_NULL:将ForeignKey设置为null;否则为false。仅当null为True时才有可能。

SET_DEFAULT:将ForeignKey设置为其默认值;必须为ForeignKey设置默认值。

Here is answer for your question that says: why we use on_delete?

When an object referenced by a ForeignKey is deleted, Django by default emulates the behavior of the SQL constraint ON DELETE CASCADE and also deletes the object containing the ForeignKey. This behavior can be overridden by specifying the on_delete argument. For example, if you have a nullable ForeignKey and you want it to be set null when the referenced object is deleted:

user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)

The possible values for on_delete are found in django.db.models:

CASCADE: Cascade deletes; the default.

PROTECT: Prevent deletion of the referenced object by raising ProtectedError, a subclass of django.db.IntegrityError.

SET_NULL: Set the ForeignKey null; this is only possible if null is True.

SET_DEFAULT: Set the ForeignKey to its default value; a default for the ForeignKey must be set.


回答 5

假设您有两种模型,一种名为Person,另一种名为Companies

根据定义,一个人可以创建多个公司。

考虑到一个公司只能有一个人,因此我们希望在删除一个人时也删除与该人关联的所有公司。

因此,我们首先创建一个Person模型,像这样

class Person(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.id+self.name

然后,公司模型如下所示

class Companies(models.Model):
    title = models.CharField(max_length=20)
    description=models.CharField(max_length=10)
    person= models.ForeignKey(Person,related_name='persons',on_delete=models.CASCADE)

注意on_delete=models.CASCADE模型公司中的用法。也就是删除拥有它的人(Person类的实例)时删除所有公司。

Let’s say you have two models, one named Person and another one named Companies.

By definition, one person can create more than one company.

Considering a company can have one and only one person, we want that when a person is deleted that all the companies associated with that person also be deleted.

So, we start by creating a Person model, like this

class Person(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.id+self.name

Then, the Companies model can look like this

class Companies(models.Model):
    title = models.CharField(max_length=20)
    description=models.CharField(max_length=10)
    person= models.ForeignKey(Person,related_name='persons',on_delete=models.CASCADE)

Notice the usage of on_delete=models.CASCADE in the model Companies. That is to delete all companies when the person that owns it (instance of class Person) is deleted.


回答 6

通过考虑将FK添加到已存在的级联(即瀑布)中来重新定向“ CASCADE”功能的思维模型。该瀑布的来源是主键。删除流向下。

因此,如果将FK的on_delete定义为“ CASCADE”,则需要将此FK的记录添加到源自PK的一系列删除中。FK的记录是否可以参与此级联(“ SET_NULL”)。实际上,带有FK的记录甚至可能阻止删除流程!用“保护”建造一个水坝。

Re-orient your mental model of the functionality of “CASCADE” by thinking of adding a FK to an already existing cascade (i.e. a waterfall). The source of this waterfall is a Primary Key. Deletes flow down.

So if you define a FK’s on_delete as “CASCADE,” you’re adding this FK’s record to a cascade of deletes originating from the PK. The FK’s record may participate in this cascade or not (“SET_NULL”). In fact, a record with a FK may even prevent the flow of the deletes! Build a dam with “PROTECT.”


回答 7

使用CASCADE意味着实际上告诉Django删除引用的记录。在下面的民意调查应用示例中:当“问题”被删除时,它还将删除该问题具有的选择。

例如:问题:您如何得知我们的?(选择:1.朋友2.电视广告3.搜索引擎4.电子邮件促销)

删除此问题时,它还将从表中删除所有这四个选项。 请注意它流动的方向。您不必放置on_delete = models。问题模型中的CASCADE将其放置在Choice中。

from django.db import models



class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.dateTimeField('date_published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_legth=200)
    votes = models.IntegerField(default=0)

Using CASCADE means actually telling Django to delete the referenced record. In the poll app example below: When a ‘Question’ gets deleted it will also delete the Choices this Question has.

e.g Question: How did you hear about us? (Choices: 1. Friends 2. TV Ad 3. Search Engine 4. Email Promotion)

When you delete this question, it will also delete all these four choices from the table. Note that which direction it flows. You don’t have to put on_delete=models.CASCADE in Question Model put it in the Choice.

from django.db import models



class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.dateTimeField('date_published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_legth=200)
    votes = models.IntegerField(default=0)

如何在Django中以GROUP BY查询?

问题:如何在Django中以GROUP BY查询?

我查询一个模型:

Members.objects.all()

它返回:

Eric, Salesman, X-Shop
Freddie, Manager, X2-Shop
Teddy, Salesman, X2-Shop
Sean, Manager, X2-Shop

我想要的是知道group_by向我的数据库发送查询的最佳Django方法,例如:

Members.objects.all().group_by('designation')

当然,这不起作用。我知道我们可以在上做一些技巧django/db/models/query.py,但我只是很好奇知道如何在不打补丁的情况下进行操作。

I query a model:

Members.objects.all()

And it returns:

Eric, Salesman, X-Shop
Freddie, Manager, X2-Shop
Teddy, Salesman, X2-Shop
Sean, Manager, X2-Shop

What I want is to know the best Django way to fire a group_by query to my database, like:

Members.objects.all().group_by('designation')

Which doesn’t work, of course. I know we can do some tricks on django/db/models/query.py, but I am just curious to know how to do it without patching.


回答 0

如果您打算进行聚合,则可以使用ORM聚合功能

from django.db.models import Count
Members.objects.values('designation').annotate(dcount=Count('designation'))

这导致查询类似于

SELECT designation, COUNT(designation) AS dcount
FROM members GROUP BY designation

并且输出将为以下形式

[{'designation': 'Salesman', 'dcount': 2}, 
 {'designation': 'Manager', 'dcount': 2}]

If you mean to do aggregation you can use the aggregation features of the ORM:

from django.db.models import Count
Members.objects.values('designation').annotate(dcount=Count('designation'))

This results in a query similar to

SELECT designation, COUNT(designation) AS dcount
FROM members GROUP BY designation

and the output would be of the form

[{'designation': 'Salesman', 'dcount': 2}, 
 {'designation': 'Manager', 'dcount': 2}]

回答 1

一个简单的解决方案,但不是正确的方法是使用原始SQL

results = Members.objects.raw('SELECT * FROM myapp_members GROUP BY designation')

另一种解决方案是使用该group_by属性:

query = Members.objects.all().query
query.group_by = ['designation']
results = QuerySet(query=query, model=Members)

现在,您可以遍历结果变量以检索结果。请注意,该group_by文档未记录,在以后的Django版本中可能会更改。

还有…为什么要使用group_by?如果不使用聚合,则可以使用order_by来获得相似的结果。

An easy solution, but not the proper way is to use raw SQL:

results = Members.objects.raw('SELECT * FROM myapp_members GROUP BY designation')

Another solution is to use the group_by property:

query = Members.objects.all().query
query.group_by = ['designation']
results = QuerySet(query=query, model=Members)

You can now iterate over the results variable to retrieve your results. Note that group_by is not documented and may be changed in future version of Django.

And… why do you want to use group_by? If you don’t use aggregation, you can use order_by to achieve an alike result.


回答 2

您也可以使用regroup模板标记按属性分组。从文档:

cities = [
    {'name': 'Mumbai', 'population': '19,000,000', 'country': 'India'},
    {'name': 'Calcutta', 'population': '15,000,000', 'country': 'India'},
    {'name': 'New York', 'population': '20,000,000', 'country': 'USA'},
    {'name': 'Chicago', 'population': '7,000,000', 'country': 'USA'},
    {'name': 'Tokyo', 'population': '33,000,000', 'country': 'Japan'},
]

...

{% regroup cities by country as country_list %}

<ul>
    {% for country in country_list %}
        <li>{{ country.grouper }}
            <ul>
            {% for city in country.list %}
                <li>{{ city.name }}: {{ city.population }}</li>
            {% endfor %}
            </ul>
        </li>
    {% endfor %}
</ul>

看起来像这样:

  • 印度
    • 孟买:19,000,000
    • 加尔各答:15,000,000
  • 美国
    • 纽约:20,000,000
    • 芝加哥:7,000,000
  • 日本
    • 东京:33,000,000

QuerySet我相信它也可以使用。

来源:https : //docs.djangoproject.com/en/2.1/ref/templates/builtins/#regroup

编辑:请注意,如果词典列表未按键排序,则该regroup标签将无法正常运行。它迭代地工作。因此,在将列表(或查询集)传递给regroup标签之前,请先按石斑鱼的键对列表进行排序。

You can also use the regroup template tag to group by attributes. From the docs:

cities = [
    {'name': 'Mumbai', 'population': '19,000,000', 'country': 'India'},
    {'name': 'Calcutta', 'population': '15,000,000', 'country': 'India'},
    {'name': 'New York', 'population': '20,000,000', 'country': 'USA'},
    {'name': 'Chicago', 'population': '7,000,000', 'country': 'USA'},
    {'name': 'Tokyo', 'population': '33,000,000', 'country': 'Japan'},
]

...

{% regroup cities by country as country_list %}

<ul>
    {% for country in country_list %}
        <li>{{ country.grouper }}
            <ul>
            {% for city in country.list %}
                <li>{{ city.name }}: {{ city.population }}</li>
            {% endfor %}
            </ul>
        </li>
    {% endfor %}
</ul>

Looks like this:

  • India
    • Mumbai: 19,000,000
    • Calcutta: 15,000,000
  • USA
    • New York: 20,000,000
    • Chicago: 7,000,000
  • Japan
    • Tokyo: 33,000,000

It also works on QuerySets I believe.

source: https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#regroup

edit: note the regroup tag does not work as you would expect it to if your list of dictionaries is not key-sorted. It works iteratively. So sort your list (or query set) by the key of the grouper before passing it to the regroup tag.


回答 3

您需要按照以下代码片段中的示例进行自定义SQL:

通过子查询自定义SQL

或在在线Django文档中显示的自定义管理器中:

添加额外的Manager方法

You need to do custom SQL as exemplified in this snippet:

Custom SQL via subquery

Or in a custom manager as shown in the online Django docs:

Adding extra Manager methods


回答 4

Django不支持免费的按组分组查询。我以非常糟糕的方式学到了它。如果不使用自定义SQL,则ORM并非旨在支持您想做的事情。您仅限于:

  • 原始sql(即MyModel.objects.raw())
  • cr.execute 句子(以及结果的手工解析)。
  • .annotate() (按句段在.annotate()的子模型中执行句子分组,例如聚合lines_count = Count(’lines’)之类的示例))。

qs您可以调用整个qs.query.group_by = ['field1', 'field2', ...]查询集,但是如果您不知道要编辑的查询,并且不能保证该查询将起作用并且不会破坏QuerySet对象的内部,则可能会有风险。此外,它是一个内部(未记录)的API,您不应直接访问它,而不必担心代码不再与将来的Django版本兼容。

Django does not support free group by queries. I learned it in the very bad way. ORM is not designed to support stuff like what you want to do, without using custom SQL. You are limited to:

  • RAW sql (i.e. MyModel.objects.raw())
  • cr.execute sentences (and a hand-made parsing of the result).
  • .annotate() (the group by sentences are performed in the child model for .annotate(), in examples like aggregating lines_count=Count(‘lines’))).

Over a queryset qs you can call qs.query.group_by = ['field1', 'field2', ...] but it is risky if you don’t know what query are you editing and have no guarantee that it will work and not break internals of the QuerySet object. Besides, it is an internal (undocumented) API you should not access directly without risking the code not being anymore compatible with future Django versions.


回答 5

有一个模块可以让您对Django模型进行分组,并仍然在结果中使用QuerySet:https : //github.com/kako-nawao/django-group-by

例如:

from django_group_by import GroupByMixin

class BookQuerySet(QuerySet, GroupByMixin):
    pass

class Book(Model):
    title = TextField(...)
    author = ForeignKey(User, ...)
    shop = ForeignKey(Shop, ...)
    price = DecimalField(...)

class GroupedBookListView(PaginationMixin, ListView):
    template_name = 'book/books.html'
    model = Book
    paginate_by = 100

    def get_queryset(self):
        return Book.objects.group_by('title', 'author').annotate(
            shop_count=Count('shop'), price_avg=Avg('price')).order_by(
            'name', 'author').distinct()

    def get_context_data(self, **kwargs):
        return super().get_context_data(total_count=self.get_queryset().count(), **kwargs)

‘book / books.html’

<ul>
{% for book in object_list %}
    <li>
        <h2>{{ book.title }}</td>
        <p>{{ book.author.last_name }}, {{ book.author.first_name }}</p>
        <p>{{ book.shop_count }}</p>
        <p>{{ book.price_avg }}</p>
    </li>
{% endfor %}
</ul>

annotate/ aggregate基本Django查询的区别在于使用了相关字段的属性,例如book.author.last_name

如果需要已分组在一起的实例的PK,请添加以下注释:

.annotate(pks=ArrayAgg('id'))

注意:ArrayAgg是Postgres特定的功能,可从Django 1.9开始使用:https : //docs.djangoproject.com/en/1.10/ref/contrib/postgres/aggregates/#arrayagg

There is module that allows you to group Django models and still work with a QuerySet in the result: https://github.com/kako-nawao/django-group-by

For example:

from django_group_by import GroupByMixin

class BookQuerySet(QuerySet, GroupByMixin):
    pass

class Book(Model):
    title = TextField(...)
    author = ForeignKey(User, ...)
    shop = ForeignKey(Shop, ...)
    price = DecimalField(...)

class GroupedBookListView(PaginationMixin, ListView):
    template_name = 'book/books.html'
    model = Book
    paginate_by = 100

    def get_queryset(self):
        return Book.objects.group_by('title', 'author').annotate(
            shop_count=Count('shop'), price_avg=Avg('price')).order_by(
            'name', 'author').distinct()

    def get_context_data(self, **kwargs):
        return super().get_context_data(total_count=self.get_queryset().count(), **kwargs)

‘book/books.html’

<ul>
{% for book in object_list %}
    <li>
        <h2>{{ book.title }}</td>
        <p>{{ book.author.last_name }}, {{ book.author.first_name }}</p>
        <p>{{ book.shop_count }}</p>
        <p>{{ book.price_avg }}</p>
    </li>
{% endfor %}
</ul>

The difference to the annotate/aggregate basic Django queries is the use of the attributes of a related field, e.g. book.author.last_name.

If you need the PKs of the instances that have been grouped together, add the following annotation:

.annotate(pks=ArrayAgg('id'))

NOTE: ArrayAgg is a Postgres specific function, available from Django 1.9 onwards: https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/aggregates/#arrayagg


回答 6

文档说您可以使用值对queryset进行分组。

class Travel(models.Model):
    interest = models.ForeignKey(Interest)
    user = models.ForeignKey(User)
    time = models.DateTimeField(auto_now_add=True)

# Find the travel and group by the interest:

>>> Travel.objects.values('interest').annotate(Count('user'))
<QuerySet [{'interest': 5, 'user__count': 2}, {'interest': 6, 'user__count': 1}]>
# the interest(id=5) had been visited for 2 times, 
# and the interest(id=6) had only been visited for 1 time.

>>> Travel.objects.values('interest').annotate(Count('user', distinct=True)) 
<QuerySet [{'interest': 5, 'user__count': 1}, {'interest': 6, 'user__count': 1}]>
# the interest(id=5) had been visited by only one person (but this person had 
#  visited the interest for 2 times

您可以找到所有书籍,并使用以下代码按名称分组:

Book.objects.values('name').annotate(Count('id')).order_by() # ensure you add the order_by()

你可以在这里看一些指南。

The document says that you can use values to group the queryset .

class Travel(models.Model):
    interest = models.ForeignKey(Interest)
    user = models.ForeignKey(User)
    time = models.DateTimeField(auto_now_add=True)

# Find the travel and group by the interest:

>>> Travel.objects.values('interest').annotate(Count('user'))
<QuerySet [{'interest': 5, 'user__count': 2}, {'interest': 6, 'user__count': 1}]>
# the interest(id=5) had been visited for 2 times, 
# and the interest(id=6) had only been visited for 1 time.

>>> Travel.objects.values('interest').annotate(Count('user', distinct=True)) 
<QuerySet [{'interest': 5, 'user__count': 1}, {'interest': 6, 'user__count': 1}]>
# the interest(id=5) had been visited by only one person (but this person had 
#  visited the interest for 2 times

You can find all the books and group them by name using this code:

Book.objects.values('name').annotate(Count('id')).order_by() # ensure you add the order_by()

You can watch some cheet sheet here.


回答 7

如果我没有记错的话,可以使用what -query-set .group_by = [‘ field ‘]

If I’m not mistaking you can use, whatever-query-set.group_by=[‘field‘]


回答 8

from django.db.models import Sum
Members.objects.annotate(total=Sum(designation))

首先,您需要导入Sum,然后..

from django.db.models import Sum
Members.objects.annotate(total=Sum(designation))

first you need to import Sum then ..