标签归档:django-models

django的model.save()为什么不调用full_clean()?

问题:django的model.save()为什么不调用full_clean()?

我只是好奇是否有人知道,除非有理由将django的orm不保存在模型中,否则它不会在模型上调用“ full_clean”。

请注意,当您调用模型的save()方法时,不会自动调用full_clean()。若要为自己创建的模型运行一步模型验证,则需要手动调用它。 django的完整档案

(注意:报价已针对Django 1.6更新…之前的django文档也对ModelForms提出了警告。)

人们为什么不希望这种行为有充分的理由?我想如果您花时间向模型添加验证,则希望每次保存模型时都运行验证。

我知道如何使一切正常工作,我只是在寻找一种解释。

I’m just curious if anyone knows if there’s good reason why django’s orm doesn’t call ‘full_clean’ on a model unless it is being saved as part of a model form.

Note that full_clean() will not be called automatically when you call your model’s save() method. You’ll need to call it manually when you want to run one-step model validation for your own manually created models. django’s full clean doc

(NOTE: quote updated for Django 1.6… previous django docs had a caveat about ModelForms as well.)

Are there good reasons why people wouldn’t want this behavior? I’d think if you took the time to add validation to a model, you’d want that validation run every time the model is saved.

I know how to get everything to work properly, I’m just looking for an explanation.


回答 0

AFAIK,这是因为向后兼容。带有排除字段的ModelForms,具有默认值的模型,pre_save()信号等也存在问题。

您可能感兴趣的资源:

AFAIK, this is because of backwards compatibility. There are also problems with ModelForms with excluded fields, models with default values, pre_save() signals, etc.

Sources you might be intrested in:


回答 1

由于考虑到兼容性,因此在django内核中未启用保存时自动清除。

如果我们正在启动一个新项目,并且希望saveModel上的默认方法可以自动清除,则可以在保存每个模型之前使用以下信号进行清除。

from django.dispatch import receiver
from django.db.models.signals import pre_save, post_save

@receiver(pre_save)
def pre_save_handler(sender, instance, *args, **kwargs):
    instance.full_clean()

Because of the compatibility considering, the auto clean on save is not enabled in django kernel.

If we are starting a new project and want the default save method on Model could clean automatically, we can use the following signal to do clean before every model was saved.

from django.dispatch import receiver
from django.db.models.signals import pre_save, post_save

@receiver(pre_save)
def pre_save_handler(sender, instance, *args, **kwargs):
    instance.full_clean()

回答 2

调用该full_clean方法的最简单方法是save在您的方法中覆盖该方法model

def save(self, *args, **kwargs):
    self.full_clean()
    return super(YourModel, self).save(*args, **kwargs)

The simpliest way to call the full_clean method is just to override save method in your model:

def save(self, *args, **kwargs):
    self.full_clean()
    return super(YourModel, self).save(*args, **kwargs)

回答 3

除了插入件的代码,声明了一个接收器的,我们可以使用一个应用程序如INSTALLED_APPS在节settings.py

INSTALLED_APPS = [
    # ...
    'django_fullclean',
    # your apps here,
]

在此之前,您可能需要django-fullclean使用PyPI 安装:

pip install django-fullclean

Instead of inserting a piece of code that declares a receiver, we can use an app as INSTALLED_APPS section in settings.py

INSTALLED_APPS = [
    # ...
    'django_fullclean',
    # your apps here,
]

Before that, you may need to install django-fullclean using PyPI:

pip install django-fullclean

回答 4

如果您要确保具有至少一个FK关系的模型,并且不想使用该模型,null=False因为这需要设置默认FK(这将是垃圾数据),那么我想出的最好方法是添加自定义.clean().save()方法。.clean()引发验证错误,并.save()调用clean。这样,可以从表单以及其他调用代码,命令行和测试中强制执行完整性。没有这个,就无法(AFAICT)编写测试来确保模型与特定选择的(非默认)其他模型具有FK关系。

class Payer(models.Model):

    name = models.CharField(blank=True, max_length=100)
    # Nullable, but will enforce FK in clean/save:
    payer_group = models.ForeignKey(PayerGroup, null=True, blank=True,)

    def clean(self):
        # Ensure every Payer is in a PayerGroup (but only via forms)
        if not self.payer_group:
            raise ValidationError(
                {'payer_group': 'Each Payer must belong to a PayerGroup.'})

    def save(self, *args, **kwargs):
        self.full_clean()
        return super().save(*args, **kwargs)

    def __str__(self):
        return self.name

If you have a model that you want to ensure has at least one FK relationship, and you don’t want to use null=False because that requires setting a default FK (which would be garbage data), the best way I’ve come up with is to add custom .clean() and .save() methods. .clean() raises the validation error, and .save() calls the clean. This way the integrity is enforced both from forms and from other calling code, the command line, and tests. Without this, there is (AFAICT) no way to write a test that ensures that a model has a FK relation to a specifically chosen (not default) other model.

class Payer(models.Model):

    name = models.CharField(blank=True, max_length=100)
    # Nullable, but will enforce FK in clean/save:
    payer_group = models.ForeignKey(PayerGroup, null=True, blank=True,)

    def clean(self):
        # Ensure every Payer is in a PayerGroup (but only via forms)
        if not self.payer_group:
            raise ValidationError(
                {'payer_group': 'Each Payer must belong to a PayerGroup.'})

    def save(self, *args, **kwargs):
        self.full_clean()
        return super().save(*args, **kwargs)

    def __str__(self):
        return self.name

回答 5

评论@Alfred Huang的回答和评论。可以通过在当前模块(models.py)中定义一个类列表并在pre_save钩子中对其进行检查来将pre_save钩子锁定到应用程序:

CUSTOM_CLASSES = [obj for name, obj in
        inspect.getmembers(sys.modules[__name__])
        if inspect.isclass(obj)]

@receiver(pre_save)
def pre_save_handler(sender, instance, **kwargs):
    if type(instance) in CUSTOM_CLASSES:
        instance.full_clean()

Commenting on @Alfred Huang’s answer and coments on it. One might lock the pre_save hook down to an app by defining a list of classes in the current module (models.py) and checking against it in the pre_save hook:

CUSTOM_CLASSES = [obj for name, obj in
        inspect.getmembers(sys.modules[__name__])
        if inspect.isclass(obj)]

@receiver(pre_save)
def pre_save_handler(sender, instance, **kwargs):
    if type(instance) in CUSTOM_CLASSES:
        instance.full_clean()

在Django模型中存储列表的最有效方法是什么?

问题:在Django模型中存储列表的最有效方法是什么?

目前,我的代码中有很多类似于以下内容的python对象:

class MyClass():
  def __init__(self, name, friends):
      self.myName = name
      self.myFriends = [str(x) for x in friends]

现在,我想将其转换为Django模型,其中self.myName是字符串字段,而self.myFriends是字符串列表。

from django.db import models

class myDjangoModelClass():
    myName = models.CharField(max_length=64)
    myFriends = ??? # what goes here?

由于列表是python中如此常见的数据结构,因此我希望它有一个Django模型字段。我知道我可以使用ManyToMany或OneToMany关系,但是我希望避免代码中的额外间接访问。

编辑:

我添加了这个相关问题,人们可能会发现它很有用。

Currently I have a lot of python objects in my code similar to the following:

class MyClass():
  def __init__(self, name, friends):
      self.myName = name
      self.myFriends = [str(x) for x in friends]

Now I want to turn this into a Django model, where self.myName is a string field, and self.myFriends is a list of strings.

from django.db import models

class myDjangoModelClass():
    myName = models.CharField(max_length=64)
    myFriends = ??? # what goes here?

Since the list is such a common data structure in python, I sort of expected there to be a Django model field for it. I know I can use a ManyToMany or OneToMany relationship, but I was hoping to avoid that extra indirection in the code.

Edit:

I added this related question, which people may find useful.


回答 0

将这种关系更好地表示为Friends表的一对多外键关系吗?我知道这myFriends只是字符串,但我认为更好的设计是创建一个Friend模型,并MyClass在结果表中包含外键。

Would this relationship not be better expressed as a one-to-many foreign key relationship to a Friends table? I understand that myFriends are just strings but I would think that a better design would be to create a Friend model and have MyClass contain a foreign key realtionship to the resulting table.


回答 1

“过早的优化是万恶之源。”

牢记这一点,让我们开始吧!一旦您的应用达到特定点,对数据进行非规范化就非常普遍。正确完成后,它可以节省大量昂贵的数据库查找,但需要多做一些整理工作。

要返回一个list朋友名称,我们需要创建一个自定义Django Field类,该类在访问时将返回一个列表。

David Cramer在他的博客上发布了有关创建SeperatedValueField的指南。这是代码:

from django.db import models

class SeparatedValuesField(models.TextField):
    __metaclass__ = models.SubfieldBase

    def __init__(self, *args, **kwargs):
        self.token = kwargs.pop('token', ',')
        super(SeparatedValuesField, self).__init__(*args, **kwargs)

    def to_python(self, value):
        if not value: return
        if isinstance(value, list):
            return value
        return value.split(self.token)

    def get_db_prep_value(self, value):
        if not value: return
        assert(isinstance(value, list) or isinstance(value, tuple))
        return self.token.join([unicode(s) for s in value])

    def value_to_string(self, obj):
        value = self._get_val_from_obj(obj)
        return self.get_db_prep_value(value)

此代码的逻辑处理从数据库到Python的序列化和反序列化值,反之亦然。现在,您可以轻松地导入并使用模型类中的自定义字段:

from django.db import models
from custom.fields import SeparatedValuesField 

class Person(models.Model):
    name = models.CharField(max_length=64)
    friends = SeparatedValuesField()

“Premature optimization is the root of all evil.”

With that firmly in mind, let’s do this! Once your apps hit a certain point, denormalizing data is very common. Done correctly, it can save numerous expensive database lookups at the cost of a little more housekeeping.

To return a list of friend names we’ll need to create a custom Django Field class that will return a list when accessed.

David Cramer posted a guide to creating a SeperatedValueField on his blog. Here is the code:

from django.db import models

class SeparatedValuesField(models.TextField):
    __metaclass__ = models.SubfieldBase

    def __init__(self, *args, **kwargs):
        self.token = kwargs.pop('token', ',')
        super(SeparatedValuesField, self).__init__(*args, **kwargs)

    def to_python(self, value):
        if not value: return
        if isinstance(value, list):
            return value
        return value.split(self.token)

    def get_db_prep_value(self, value):
        if not value: return
        assert(isinstance(value, list) or isinstance(value, tuple))
        return self.token.join([unicode(s) for s in value])

    def value_to_string(self, obj):
        value = self._get_val_from_obj(obj)
        return self.get_db_prep_value(value)

The logic of this code deals with serializing and deserializing values from the database to Python and vice versa. Now you can easily import and use our custom field in the model class:

from django.db import models
from custom.fields import SeparatedValuesField 

class Person(models.Model):
    name = models.CharField(max_length=64)
    friends = SeparatedValuesField()

回答 2

在Django中存储列表的一种简单方法是将其转换为JSON字符串,然后将其另存为模型中的Text。然后,您可以通过将(JSON)字符串转换回python列表来检索列表。这是如何做:

“列表”将存储在您的Django模型中,如下所示:

class MyModel(models.Model):
    myList = models.TextField(null=True) # JSON-serialized (text) version of your list

在您的视图/控制器代码中:

将列表存储在数据库中:

import simplejson as json # this would be just 'import json' in Python 2.7 and later
...
...

myModel = MyModel()
listIWantToStore = [1,2,3,4,5,'hello']
myModel.myList = json.dumps(listIWantToStore)
myModel.save()

从数据库中检索列表:

jsonDec = json.decoder.JSONDecoder()
myPythonList = jsonDec.decode(myModel.myList)

从概念上讲,这是正在发生的事情:

>>> myList = [1,2,3,4,5,'hello']
>>> import simplejson as json
>>> myJsonList = json.dumps(myList)
>>> myJsonList
'[1, 2, 3, 4, 5, "hello"]'
>>> myJsonList.__class__
<type 'str'>
>>> jsonDec = json.decoder.JSONDecoder()
>>> myPythonList = jsonDec.decode(myJsonList)
>>> myPythonList
[1, 2, 3, 4, 5, u'hello']
>>> myPythonList.__class__
<type 'list'>

A simple way to store a list in Django is to just convert it into a JSON string, and then save that as Text in the model. You can then retrieve the list by converting the (JSON) string back into a python list. Here’s how:

The “list” would be stored in your Django model like so:

class MyModel(models.Model):
    myList = models.TextField(null=True) # JSON-serialized (text) version of your list

In your view/controller code:

Storing the list in the database:

import simplejson as json # this would be just 'import json' in Python 2.7 and later
...
...

myModel = MyModel()
listIWantToStore = [1,2,3,4,5,'hello']
myModel.myList = json.dumps(listIWantToStore)
myModel.save()

Retrieving the list from the database:

jsonDec = json.decoder.JSONDecoder()
myPythonList = jsonDec.decode(myModel.myList)

Conceptually, here’s what’s going on:

>>> myList = [1,2,3,4,5,'hello']
>>> import simplejson as json
>>> myJsonList = json.dumps(myList)
>>> myJsonList
'[1, 2, 3, 4, 5, "hello"]'
>>> myJsonList.__class__
<type 'str'>
>>> jsonDec = json.decoder.JSONDecoder()
>>> myPythonList = jsonDec.decode(myJsonList)
>>> myPythonList
[1, 2, 3, 4, 5, u'hello']
>>> myPythonList.__class__
<type 'list'>

回答 3

如果您将Django> = 1.9与Postgres一起使用,可以利用ArrayField的优势

用于存储数据列表的字段。可以使用大多数字段类型,您只需将另一个字段实例作为base_field传递即可。您也可以指定尺寸。可以嵌套ArrayField来存储多维数组。

也可以嵌套数组字段:

from django.contrib.postgres.fields import ArrayField
from django.db import models

class ChessBoard(models.Model):
    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )

正如@ thane-brimhall所提到的,也可以直接查询元素。文档参考

If you are using Django >= 1.9 with Postgres you can make use of ArrayField advantages

A field for storing lists of data. Most field types can be used, you simply pass another field instance as the base_field. You may also specify a size. ArrayField can be nested to store multi-dimensional arrays.

It is also possible to nest array fields:

from django.contrib.postgres.fields import ArrayField
from django.db import models

class ChessBoard(models.Model):
    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )

As @thane-brimhall mentioned it is also possible to query elements directly. Documentation reference


回答 4

由于这是一个古老的问题,自此之后Django技术必定发生了重大变化,因此该答案反映了Django 1.4版,并且很可能适用于v 1.5。

Django默认使用关系数据库。您应该利用’em。使用ManyToManyField将友谊映射到数据库关系(外键约束)。这样做使您可以将RelatedManagers用于使用智能查询集的朋友列表。您可以使用所有可用的方法,例如filtervalues_list

使用ManyToManyField关系和属性:

class MyDjangoClass(models.Model):
    name = models.CharField(...)
    friends = models.ManyToManyField("self")

    @property
    def friendlist(self):
        # Watch for large querysets: it loads everything in memory
        return list(self.friends.all())

您可以通过以下方式访问用户的朋友列表:

joseph = MyDjangoClass.objects.get(name="Joseph")
friends_of_joseph = joseph.friendlist

但是请注意,这些关系是对称的:如果约瑟夫是鲍勃的朋友,那么鲍勃是约瑟夫的朋友。

As this is an old question, and Django techniques must have changed significantly since, this answer reflects Django version 1.4, and is most likely applicable for v 1.5.

Django by default uses relational databases; you should make use of ’em. Map friendships to database relations (foreign key constraints) with the use of ManyToManyField. Doing so allows you to use RelatedManagers for friendlists, which use smart querysets. You can use all available methods such as filter or values_list.

Using ManyToManyField relations and properties:

class MyDjangoClass(models.Model):
    name = models.CharField(...)
    friends = models.ManyToManyField("self")

    @property
    def friendlist(self):
        # Watch for large querysets: it loads everything in memory
        return list(self.friends.all())

You can access a user’s friend list this way:

joseph = MyDjangoClass.objects.get(name="Joseph")
friends_of_joseph = joseph.friendlist

Note however that these relations are symmetrical: if Joseph is a friend of Bob, then Bob is a friend of Joseph.


回答 5

class Course(models.Model):
   name = models.CharField(max_length=256)
   students = models.ManyToManyField(Student)

class Student(models.Model):
   first_name = models.CharField(max_length=256)
   student_number = models.CharField(max_length=128)
   # other fields, etc...

   friends = models.ManyToManyField('self')
class Course(models.Model):
   name = models.CharField(max_length=256)
   students = models.ManyToManyField(Student)

class Student(models.Model):
   first_name = models.CharField(max_length=256)
   student_number = models.CharField(max_length=128)
   # other fields, etc...

   friends = models.ManyToManyField('self')

回答 6

请记住,这最终必须在关系数据库中结束。因此,使用关系确实解决此问题的常用方法。如果绝对要在对象本身中存储列表,则可以使用逗号分隔列表,然后将其存储在字符串中,然后提供将字符串拆分为列表的访问器函数。这样,您将被限制为最大数量的字符串,并且您将失去有效的查询。

Remember that this eventually has to end up in a relational database. So using relations really is the common way to solve this problem. If you absolutely insist on storing a list in the object itself, you could make it for example comma-separated, and store it in a string, and then provide accessor functions that split the string into a list. With that, you will be limited to a maximum number of strings, and you will lose efficient queries.


回答 7

如果您使用的是postgres,则可以使用以下代码:

class ChessBoard(models.Model):

    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )

如果您需要更多详细信息,请阅读以下链接:https : //docs.djangoproject.com/pt-br/1.9/ref/contrib/postgres/fields/

In case you’re using postgres, you can use something like this:

class ChessBoard(models.Model):

    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )

if you need more details you can read in the link below: https://docs.djangoproject.com/pt-br/1.9/ref/contrib/postgres/fields/


回答 8

您可以使用Django Pickle Field来存储几乎任何对象,例如以下代码段:

http://www.djangosnippets.org/snippets/513/

You can store virtually any object using a Django Pickle Field, ala this snippet:

http://www.djangosnippets.org/snippets/513/


回答 9

在Django模型中存储字符串列表:

class Bar(models.Model):
    foo = models.TextField(blank=True)

    def set_list(self, element):
        if self.foo:
            self.foo = self.foo + "," + element
        else:
            self.foo = element

    def get_list(self):
        if self.foo:
            return self.foo.split(",")
        else:
            None

您可以这样称呼它:

bars = Bar()
bars.set_list("str1")
bars.set_list("str2")
list = bars.get_list()
if list is not None:
    for bar in list:
        print bar
else:
    print "List is empty."      

Storing a list of strings in Django model:

class Bar(models.Model):
    foo = models.TextField(blank=True)

    def set_list(self, element):
        if self.foo:
            self.foo = self.foo + "," + element
        else:
            self.foo = element

    def get_list(self):
        if self.foo:
            return self.foo.split(",")
        else:
            None

and you can call it like this:

bars = Bar()
bars.set_list("str1")
bars.set_list("str2")
list = bars.get_list()
if list is not None:
    for bar in list:
        print bar
else:
    print "List is empty."      

回答 10

我的解决方案可能是它可以帮助某人:

import json
from django.db import models


class ExampleModel(models.Model):
    _list = models.TextField(default='[]')

    @property
    def list(self):
        return json.loads(self._list)

    @list.setter
    def list(self, value):
        self._list = json.dumps(self.list + value)

My solution, may be it helps someone:

import json
from django.db import models


class ExampleModel(models.Model):
    _list = models.TextField(default='[]')

    @property
    def list(self):
        return json.loads(self._list)

    @list.setter
    def list(self, value):
        self._list = json.dumps(self.list + value)

回答 11

使用一对多关系(从Friend到父类的FK)将使您的应用程序更具可伸缩性(因为您可以使用简单名称之外的其他属性来简单地扩展Friend对象)。因此这是最好的方法

Using one-to-many relation (FK from Friend to parent class) will make your app more scalable (as you can trivially extend the Friend object with additional attributes beyond the simple name). And thus this is the best way


使用Django / South重命名模型的最简单方法?

问题:使用Django / South重命名模型的最简单方法?

我一直在South的网站,Google和SO上寻找答案,但是找不到简单的方法来做到这一点。

我想使用South重命名Django模型。说您有以下几点:

class Foo(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Foo)

并且您想要将Foo转换为Bar,即

class Bar(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Bar)

为简单起见,我只是尝试将名称从更改FooBar,但现在忽略其中的foo成员FooTwo

使用South进行此操作最简单的方法是什么?

  1. 我可能可以进行数据迁移,但这似乎很复杂。
  2. 编写一个自定义迁移,例如db.rename_table('city_citystate', 'geo_citystate'),但是在这种情况下我不确定如何修复外键。
  3. 您知道一种更简单的方法吗?

I’ve been hunting for an answer to this on South’s site, Google, and SO, but couldn’t find a simple way to do this.

I want to rename a Django model using South. Say you have the following:

class Foo(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Foo)

and you want to convert Foo to Bar, namely

class Bar(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Bar)

To keep it simple, I’m just trying to change the name from Foo to Bar, but ignore the foo member in FooTwo for now.

What’s the easiest way to do this using South?

  1. I could probably do a data migration, but that seems pretty involved.
  2. Write a custom migration, e.g. db.rename_table('city_citystate', 'geo_citystate'), but I’m not sure how to fix the foreign key in this case.
  3. An easier way that you know?

回答 0

为了回答您的第一个问题,简单的模型/表重命名非常简单。运行命令:

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(更新2:尝试--auto,而不是--empty避免低于警告感谢@KFB的提示。)

如果您使用的是南方的旧版本,则需要startmigration而不是schemamigration

然后手动编辑迁移文件,如下所示:

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.rename_table('yourapp_foo', 'yourapp_bar')


    def backwards(self, orm):
        db.rename_table('yourapp_bar','yourapp_foo')   

您可以使用db_table模型类中的Meta选项来更简单地完成此操作。但是每次这样做,都增加了代码库的旧版权重-类名与表名不同会使代码难以理解和维护。为了清楚起见,我完全支持进行这样的简单重构。

(更新)我刚刚在生产环境中尝试过此操作,并在应用迁移时收到一个奇怪的警告。它说:

The following content types are stale and need to be deleted:

    yourapp | foo

Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.

我回答“不”,一切似乎都很好。

To answer your first question, the simple model/table rename is pretty straightforward. Run the command:

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(Update 2: try --auto instead of --empty to avoid the warning below. Thanks to @KFB for the tip.)

If you’re using an older version of south, you’ll need startmigration instead of schemamigration.

Then manually edit the migration file to look like this:

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.rename_table('yourapp_foo', 'yourapp_bar')


    def backwards(self, orm):
        db.rename_table('yourapp_bar','yourapp_foo')   

You can accomplish this more simply using the db_table Meta option in your model class. But every time you do that, you increase the legacy weight of your codebase — having class names differ from table names makes your code harder to understand and maintain. I fully support doing simple refactorings like this for the sake of clarity.

(update) I just tried this in production, and got a strange warning when I went to apply the migration. It said:

The following content types are stale and need to be deleted:

    yourapp | foo

Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.

I answered “no” and everything seemed to be fine.


回答 1

进行更改models.py,然后运行

./manage.py schemamigration --auto myapp

检查迁移文件时,您会看到它删除了一个表并创建了一个新表。

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Deleting model 'Foo'                                                                                                                      
        db.delete_table('myapp_foo')

        # Adding model 'Bar'                                                                                                                        
        db.create_table('myapp_bar', (
        ...
        ))
        db.send_create_signal('myapp', ['Bar'])

    def backwards(self, orm):
        ...

这不是您想要的。而是编辑迁移,使其看起来像:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming model from 'Foo' to 'Bar'                                                                                                                      
        db.rename_table('myapp_foo', 'myapp_bar')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(
                app_label='myapp', model='foo').update(model='bar')

    def backwards(self, orm):
        # Renaming model from 'Bar' to 'Foo'                                                                                                                      
        db.rename_table('myapp_bar', 'myapp_foo')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(app_label='myapp', model='bar').update(model='foo')

在没有该update语句的情况下,该db.send_create_signal调用将ContentType使用新的模型名称创建一个新的模型。但它最好只updateContentType你已经拥有的情况下有数据库对象指向它(例如,通过一GenericForeignKey)。

另外,如果您已经重命名了某些列,这些列是重命名模型的外键,请不要忘记

db.rename_column(myapp_model, foo_id, bar_id)

Make the changes in models.py and then run

./manage.py schemamigration --auto myapp

When you inspect the migration file, you’ll see that it deletes a table and creates a new one

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Deleting model 'Foo'                                                                                                                      
        db.delete_table('myapp_foo')

        # Adding model 'Bar'                                                                                                                        
        db.create_table('myapp_bar', (
        ...
        ))
        db.send_create_signal('myapp', ['Bar'])

    def backwards(self, orm):
        ...

This is not quite what you want. Instead, edit the migration so that it looks like:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming model from 'Foo' to 'Bar'                                                                                                                      
        db.rename_table('myapp_foo', 'myapp_bar')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(
                app_label='myapp', model='foo').update(model='bar')

    def backwards(self, orm):
        # Renaming model from 'Bar' to 'Foo'                                                                                                                      
        db.rename_table('myapp_bar', 'myapp_foo')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(app_label='myapp', model='bar').update(model='foo')

In the absence of the update statement, the db.send_create_signal call will create a new ContentType with the new model name. But it’s better to just update the ContentType you already have in case there are database objects pointing to it (e.g., via a GenericForeignKey).

Also, if you’ve renamed some columns which are foreign keys to the renamed model, don’t forget to

db.rename_column(myapp_model, foo_id, bar_id)

回答 2

南方本身不能做-怎么知道这Bar代表Foo过去?我将为此编写自定义迁移。您可以ForeignKey像上面所做的那样更改in代码,然后只是重命名适当的字段和表的一种情况,您可以根据需要进行任何操作。

最后,您真的需要这样做吗?我还不需要重命名模型-模型名称只是实现细节-特别是考虑到verbose_nameMeta选项的可用性。

South can’t do it itself – how does it know that Bar represents what Foo used to? This is the sort of thing I’d write a custom migration for. You can change your ForeignKey in code as you’ve done above, and then it’s just a case of renaming the appropriate fields and tables, which you can do any way you want.

Finally, do you really need to do this? I’ve yet to need to rename models – model names are just an implementation detail – particularly given the availability of the verbose_name Meta option.


回答 3

我遵循了上面Leopd的解决方案。但是,这并没有更改型号名称。我在代码中手动更改了它(在相关模型中也将其称为FK)。并进行了另一个南迁,但带有–fake选项。这使得模型名称和表名称相同。

刚意识到,可以先从更改模型名称开始,然后在应用迁移文件之前编辑迁移文件。干净得多。

I followed Leopd’s solution above. But, that did not change the model names. I changed it manually in the code (also in related models where this is referred as FK). And done another south migration, but with –fake option. This makes model names and table names to be same.

Just realized, one could first start with changing model names, then edit the migrations file before applying them. Much cleaner.


如何在Django中创建子弹?

问题:如何在Django中创建子弹?

我正在尝试SlugField在Django中创建一个。

我创建了这个简单的模型:

from django.db import models

class Test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField()

然后,我这样做:

>>> from mysite.books.models import Test
>>> t=Test(q="aa a a a", s="b b b b")
>>> t.s
'b b b b'
>>> t.save()
>>> t.s
'b b b b'

我在期待b-b-b-b

I am trying to create a SlugField in Django.

I created this simple model:

from django.db import models

class Test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField()

I then do this:

>>> from mysite.books.models import Test
>>> t=Test(q="aa a a a", s="b b b b")
>>> t.s
'b b b b'
>>> t.save()
>>> t.s
'b b b b'

I was expecting b-b-b-b.


回答 0

您将需要使用Slugify函数。

>>> from django.template.defaultfilters import slugify
>>> slugify("b b b b")
u'b-b-b-b'
>>>

您可以slugify通过覆盖save方法自动调用:

class Test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField()

    def save(self, *args, **kwargs):
        self.s = slugify(self.q)
        super(Test, self).save(*args, **kwargs)

请注意,以上内容将导致您在修改q字段时更改您的URL ,这可能会导致链接断开。创建新对象时最好只生成一次子弹:

class Test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField()

    def save(self, *args, **kwargs):
        if not self.id:
            # Newly created object, so set slug
            self.s = slugify(self.q)

        super(Test, self).save(*args, **kwargs)

You will need to use the slugify function.

>>> from django.template.defaultfilters import slugify
>>> slugify("b b b b")
u'b-b-b-b'
>>>

You can call slugify automatically by overriding the save method:

class Test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField()
    
    def save(self, *args, **kwargs):
        self.s = slugify(self.q)
        super(Test, self).save(*args, **kwargs)

Be aware that the above will cause your URL to change when the q field is edited, which can cause broken links. It may be preferable to generate the slug only once when you create a new object:

class Test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField()
    
    def save(self, *args, **kwargs):
        if not self.id:
            # Newly created object, so set slug
            self.s = slugify(self.q)

        super(Test, self).save(*args, **kwargs)

回答 1

有一些utf-8字符的特殊情况

例:

>>> from django.template.defaultfilters import slugify
>>> slugify(u"test ąęśćółń")
u'test-aescon' # there is no "l"

这可以用Unidecode解决

>>> from unidecode import unidecode
>>> from django.template.defaultfilters import slugify
>>> slugify(unidecode(u"test ąęśćółń"))
u'test-aescoln'

There is corner case with some utf-8 characters

Example:

>>> from django.template.defaultfilters import slugify
>>> slugify(u"test ąęśćółń")
u'test-aescon' # there is no "l"

This can be solved with Unidecode

>>> from unidecode import unidecode
>>> from django.template.defaultfilters import slugify
>>> slugify(unidecode(u"test ąęśćółń"))
u'test-aescoln'

回答 2

对Thepeer答案的一个小修正:要覆盖save()模型类中的函数,最好向其添加参数:

from django.utils.text import slugify

def save(self, *args, **kwargs):
    if not self.id:
        self.s = slugify(self.q)

    super(test, self).save(*args, **kwargs)

否则,test.objects.create(q="blah blah blah")将导致force_insert错误(意外参数)。

A small correction to Thepeer’s answer: To override save() function in model classes, better add arguments to it:

from django.utils.text import slugify

def save(self, *args, **kwargs):
    if not self.id:
        self.s = slugify(self.q)

    super(test, self).save(*args, **kwargs)

Otherwise, test.objects.create(q="blah blah blah") will result in a force_insert error (unexpected argument).


回答 3

如果您使用的管理界面增加模型的新项目,你可以建立一个ModelAdmin在你admin.py和利用prepopulated_fields自动进入蛞蝓的:

class ClientAdmin(admin.ModelAdmin):
    prepopulated_fields = {'slug': ('name',)}

admin.site.register(Client, ClientAdmin)

在这里,当用户在admin表单中为该name字段输入值时,slug将使用正确的slugified自动填充name

If you’re using the admin interface to add new items of your model, you can set up a ModelAdmin in your admin.py and utilize prepopulated_fields to automate entering of a slug:

class ClientAdmin(admin.ModelAdmin):
    prepopulated_fields = {'slug': ('name',)}

admin.site.register(Client, ClientAdmin)

Here, when the user enters a value in the admin form for the name field, the slug will be automatically populated with the correct slugified name.


回答 4

在大多数情况下,该条块不应更改,因此您实际上只想在首次保存时进行计算:

class Test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField(editable=False) # hide from admin

    def save(self):
        if not self.id:
            self.s = slugify(self.q)

        super(Test, self).save()

In most cases the slug should not change, so you really only want to calculate it on first save:

class Test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField(editable=False) # hide from admin

    def save(self):
        if not self.id:
            self.s = slugify(self.q)

        super(Test, self).save()

回答 5

使用prepopulated_fields在您的管理类:

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}

admin.site.register(Article, ArticleAdmin)

Use prepopulated_fields in your admin class:

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}

admin.site.register(Article, ArticleAdmin)

回答 6

如果您不想将slugfield设置为“不可编辑”,那么我相信您希望将Null和Blank属性设置为False。否则,尝试保存到Admin时会收到错误消息。

因此,对上述示例的修改如下:

class test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField(null=True, blank=True) # Allow blank submission in admin.

    def save(self):
        if not self.id:
            self.s = slugify(self.q)

        super(test, self).save()

If you don’t want to set the slugfield to Not be editable, then I believe you’ll want to set the Null and Blank properties to False. Otherwise you’ll get an error when trying to save in Admin.

So a modification to the above example would be::

class test(models.Model):
    q = models.CharField(max_length=30)
    s = models.SlugField(null=True, blank=True) # Allow blank submission in admin.

    def save(self):
        if not self.id:
            self.s = slugify(self.q)

        super(test, self).save()

回答 7

我正在使用Django 1.7

像这样在模型中创建一个SlugField:

slug = models.SlugField()

然后在admin.py定义prepopulated_fields;

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}

I’m using Django 1.7

Create a SlugField in your model like this:

slug = models.SlugField()

Then in admin.py define prepopulated_fields;

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}

回答 8

您可以查看的文档,SlugField以更具描述性的方式进一步了解它。

You can look at the docs for the SlugField to get to know more about it in more descriptive way.


Django-如何使用South重命名模型字段?

问题:Django-如何使用South重命名模型字段?

我想更改模型中特定字段的名称:

class Foo(models.Model):
    name = models.CharField()
    rel  = models.ForeignKey(Bar)

应更改为:

class Foo(models.Model):
    full_name     = models.CharField()
    odd_relation  = models.ForeignKey(Bar)

使用South进行此操作最简单的方法是什么?

I would like to change a name of specific fields in a model:

class Foo(models.Model):
    name = models.CharField()
    rel  = models.ForeignKey(Bar)

should change to:

class Foo(models.Model):
    full_name     = models.CharField()
    odd_relation  = models.ForeignKey(Bar)

What’s the easiest way to do this using South?


回答 0

您可以使用该db.rename_column功能。

class Migration:

    def forwards(self, orm):
        # Rename 'name' field to 'full_name'
        db.rename_column('app_foo', 'name', 'full_name')




    def backwards(self, orm):
        # Rename 'full_name' field to 'name'
        db.rename_column('app_foo', 'full_name', 'name')

表的第一个参数db.rename_column是表名,因此记住Django如何创建表名很重要:

Django会自动从您的模型类的名称和包含该表的应用程序中获取数据库表的名称。通过将模型的“应用程序标签”(即您在manage.py startapp中使用的名称)连接到模型的类名称,并在其之间加下划线,来构造模型的数据库表名称。

在你有一个多措辞,骆驼套管型号名称的情况下,如项目项,表名会app_projectitem(即下划线不会之间插入projectitem即使它们是骆驼式大小写)。

You can use the db.rename_column function.

class Migration:

    def forwards(self, orm):
        # Rename 'name' field to 'full_name'
        db.rename_column('app_foo', 'name', 'full_name')




    def backwards(self, orm):
        # Rename 'full_name' field to 'name'
        db.rename_column('app_foo', 'full_name', 'name')

The first argument of db.rename_column is the table name, so it’s important to remember how Django creates table names:

Django automatically derives the name of the database table from the name of your model class and the app that contains it. A model’s database table name is constructed by joining the model’s “app label” — the name you used in manage.py startapp — to the model’s class name, with an underscore between them.

In the case where you have a multi-worded, camel-cased model name, such as ProjectItem, the table name will be app_projectitem (i.e., an underscore will not be inserted between project and item even though they are camel-cased).


回答 1

这是我的工作:

  1. 在模型中更改列名(在本示例中为myapp/models.py
  2. ./manage.py schemamigration myapp renaming_column_x --auto

注意renaming_column_x可以是任何您喜欢的东西,它只是给迁移文件起一个描述性名称的一种方式。

这将为您生成一个名为的文件myapp/migrations/000x_renaming_column_x.py,该文件将删除您的旧列并添加一个新列。

修改此文件中的代码,以将迁移行为更改为简单的重命名:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming column 'mymodel.old_column_name' to 'mymodel.new_column_name'
        db.rename_column(u'myapp_mymodel', 'old_column_name', 'new_column_name')

    def backwards(self, orm):
        # Renaming column 'mymodel.new_column_name' to 'mymodel.old_column_name'
        db.rename_column(u'myapp_mymodel', 'new_column_name', 'old_column_name')

Here’s what I do:

  1. Make the column name change in your model (in this example it would be myapp/models.py)
  2. Run ./manage.py schemamigration myapp renaming_column_x --auto

Note renaming_column_x can be anything you like, it’s just a way of giving a descriptive name to the migration file.

This will generate you a file called myapp/migrations/000x_renaming_column_x.py which will delete your old column and add a new column.

Modify the code in this file to change the migration behaviour to a simple rename:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming column 'mymodel.old_column_name' to 'mymodel.new_column_name'
        db.rename_column(u'myapp_mymodel', 'old_column_name', 'new_column_name')

    def backwards(self, orm):
        # Renaming column 'mymodel.new_column_name' to 'mymodel.old_column_name'
        db.rename_column(u'myapp_mymodel', 'new_column_name', 'old_column_name')

回答 2

我不知道db.rename列,听起来很方便,但是在过去,我将新列添加为一个schemamigration,然后创建了一个datamigration将值移到新字段中,然后创建了另一个schemamigration以删除旧列。

I didn’t know about db.rename column, sounds handy, however in the past I have added the new column as one schemamigration, then created a datamigration to move values into the new field, then a second schemamigration to remove the old column


回答 3

Django 1.7引入了Migrations,所以现在您甚至不需要安装额外的软件包即可管理迁移。

要重命名模型,您需要首先创建空迁移:

$ manage.py makemigrations <app_name> --empty

然后,您需要像这样编辑迁移代码:

from django.db import models, migrations

class Migration(migrations.Migration):

dependencies = [
    ('yourapp', 'XXXX_your_previous_migration'),
]

operations = [
    migrations.RenameField(
        model_name='Foo',
        old_name='name',
        new_name='full_name'
    ),
    migrations.RenameField(
        model_name='Foo',
        old_name='rel',
        new_name='odd_relation'
    ),
]

然后,您需要运行:

$ manage.py migrate <app_name>

Django 1.7 introduced Migrations so now you don’t even need to install extra package to manage your migrations.

To rename your model you need to create empty migration first:

$ manage.py makemigrations <app_name> --empty

Then you need to edit your migration’s code like this:

from django.db import models, migrations

class Migration(migrations.Migration):

dependencies = [
    ('yourapp', 'XXXX_your_previous_migration'),
]

operations = [
    migrations.RenameField(
        model_name='Foo',
        old_name='name',
        new_name='full_name'
    ),
    migrations.RenameField(
        model_name='Foo',
        old_name='rel',
        new_name='odd_relation'
    ),
]

And after that you need to run:

$ manage.py migrate <app_name>

回答 4

只需更改模型并makemigrations在1.9中运行

Django自动检测到您已删除并创建了一个字段,并询问:

Did you rename model.old to model.new (a IntegerField)? [y/N]

同意,就可以创建正确的迁移。魔法。

Just change the model and run makemigrations in 1.9

Django automatically detects that you’ve deleted and created a single field, and asks:

Did you rename model.old to model.new (a IntegerField)? [y/N]

Say yes, and the right migration gets created. Magic.


回答 5

  1. south在项目设置文件中添加到已安装的应用程序。
  2. 注释掉添加/修改的字段/表。
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. 取消注释该字段并写入修改后的字段
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

如果使用的是“ pycharm”,则可以使用“ ctrl + shift + r”代替“ manage.py”,并使用“ shift”作为参数。

  1. Add south to your installed apps in project setting file.
  2. Comment out the added/modified field/table.
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. Un-comment the field and write the modified one
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

If you are using ‘pycharm’, then you can use ‘ctrl+shift+r’ instead of ‘manage.py’ , and ‘shift ‘ for parameters.


以编程方式将图像保存到Django ImageField

问题:以编程方式将图像保存到Django ImageField

好的,我已经尝试了几乎所有内容,但无法正常工作。

  • 我有一个上面带有ImageField的Django模型
  • 我有通过HTTP下载图像的代码(已测试并且可以工作)
  • 图像直接保存到“ upload_to”文件夹中(upload_to是在ImageField上设置的文件夹)
  • 我需要做的就是将已经存在的图像文件路径与ImageField相关联

我已经用6种不同的方式编写了这段代码。

我遇到的问题是我正在编写的所有代码均导致以下行为:(1)Django将创建第二个文件,(2)重命名新文件,在文件末尾添加_名称,然后(3)不会在保留基本为空的重命名文件的情况下传输任何数据。在“ upload_to”路径中剩下的是2个文件,一个是实际图像,一个是图像名称,但为空,当然ImageField路径设置为Django尝试创建的空文件。 。

如果不清楚,我将尝试说明:

## Image generation code runs.... 
/Upload
     generated_image.jpg     4kb

## Attempt to set the ImageField path...
/Upload
     generated_image.jpg     4kb
     generated_image_.jpg    0kb

ImageField.Path = /Upload/generated_image_.jpg

如何在不让Django尝试重新存储文件的情况下执行此操作?我真正想要的就是这种效果……

model.ImageField.path = generated_image_path

…但是那当然是行不通的。

是的,我已经经历这里的其他问题,如走了这一个,以及对Django的DOC 文件

更新 在进一步测试之后,仅当在Windows Server上的Apache下运行时,它才会执行此行为。在XP上的“ runserver”下运行时,它不会执行此行为。

我很沮丧

这是在XP上成功运行的代码…

f = open(thumb_path, 'r')
model.thumbnail = File(f)
model.save()

Ok, I’ve tried about near everything and I cannot get this to work.

  • I have a Django model with an ImageField on it
  • I have code that downloads an image via HTTP (tested and works)
  • The image is saved directly into the ‘upload_to’ folder (the upload_to being the one that is set on the ImageField)
  • All I need to do is associate the already existing image file path with the ImageField

I’ve written this code about 6 different ways.

The problem I’m running into is all of the code that I’m writing results in the following behavior: (1) Django will make a 2nd file, (2) rename the new file, adding an _ to the end of the file name, then (3) not transfer any of the data over leaving it basically an empty re-named file. What’s left in the ‘upload_to’ path is 2 files, one that is the actual image, and one that is the name of the image,but is empty, and of course the ImageField path is set to the empty file that Django try to create.

In case that was unclear, I’ll try to illustrate:

## Image generation code runs.... 
/Upload
     generated_image.jpg     4kb

## Attempt to set the ImageField path...
/Upload
     generated_image.jpg     4kb
     generated_image_.jpg    0kb

ImageField.Path = /Upload/generated_image_.jpg

How can I do this without having Django try to re-store the file? What I’d really like is something to this effect…

model.ImageField.path = generated_image_path

…but of course that doesn’t work.

And yes I’ve gone through the other questions here like this one as well as the django doc on File

UPDATE After further testing, it only does this behavior when running under Apache on Windows Server. While running under the ‘runserver’ on XP it does not execute this behavior.

I am stumped.

Here is the code which runs successfully on XP…

f = open(thumb_path, 'r')
model.thumbnail = File(f)
model.save()

回答 0

我有一些代码可以从网络上获取图像并将其存储在模型中。重要的位是:

from django.core.files import File  # you need this somewhere
import urllib


# The following actually resides in a method of my model

result = urllib.urlretrieve(image_url) # image_url is a URL to an image

# self.photo is the ImageField
self.photo.save(
    os.path.basename(self.url),
    File(open(result[0], 'rb'))
    )

self.save()

这有点令人困惑,因为它脱离了我的模型并且脱离了上下文,但是重要的部分是:

  • 从Web提取的图像存储在upload_to文件夹中,而是由urllib.urlretrieve()作为临时文件存储,之后被丢弃。
  • ImageField.save()方法采用文件名(os.path.basename位)和django.core.files.File对象。

让我知道您是否有疑问或需要澄清。

编辑:为清楚起见,这是模型(减去任何必需的import语句):

class CachedImage(models.Model):
    url = models.CharField(max_length=255, unique=True)
    photo = models.ImageField(upload_to=photo_path, blank=True)

    def cache(self):
        """Store image locally if we have a URL"""

        if self.url and not self.photo:
            result = urllib.urlretrieve(self.url)
            self.photo.save(
                    os.path.basename(self.url),
                    File(open(result[0], 'rb'))
                    )
            self.save()

I have some code that fetches an image off the web and stores it in a model. The important bits are:

from django.core.files import File  # you need this somewhere
import urllib


# The following actually resides in a method of my model

result = urllib.urlretrieve(image_url) # image_url is a URL to an image

# self.photo is the ImageField
self.photo.save(
    os.path.basename(self.url),
    File(open(result[0], 'rb'))
    )

self.save()

That’s a bit confusing because it’s pulled out of my model and a bit out of context, but the important parts are:

  • The image pulled from the web is not stored in the upload_to folder, it is instead stored as a tempfile by urllib.urlretrieve() and later discarded.
  • The ImageField.save() method takes a filename (the os.path.basename bit) and a django.core.files.File object.

Let me know if you have questions or need clarification.

Edit: for the sake of clarity, here is the model (minus any required import statements):

class CachedImage(models.Model):
    url = models.CharField(max_length=255, unique=True)
    photo = models.ImageField(upload_to=photo_path, blank=True)

    def cache(self):
        """Store image locally if we have a URL"""

        if self.url and not self.photo:
            result = urllib.urlretrieve(self.url)
            self.photo.save(
                    os.path.basename(self.url),
                    File(open(result[0], 'rb'))
                    )
            self.save()

回答 1

如果尚未创建模型,则超级简单:

首先,将您的图片文件复制到上传路径(在以下代码段中假定为‘path /’)。

其次,使用类似:

class Layout(models.Model):
    image = models.ImageField('img', upload_to='path/')

layout = Layout()
layout.image = "path/image.png"
layout.save()

在django 1.4中进行了测试和工作,它可能也适用于现有模型。

Super easy if model hasn’t been created yet:

First, copy your image file to the upload path (assumed = ‘path/’ in following snippet).

Second, use something like:

class Layout(models.Model):
    image = models.ImageField('img', upload_to='path/')

layout = Layout()
layout.image = "path/image.png"
layout.save()

tested and working in django 1.4, it might work also for an existing model.


回答 2

只是一点点。tvon答案有效,但是,如果您在Windows上工作,则可能需要open()使用'rb'。像这样:

class CachedImage(models.Model):
    url = models.CharField(max_length=255, unique=True)
    photo = models.ImageField(upload_to=photo_path, blank=True)

    def cache(self):
        """Store image locally if we have a URL"""

        if self.url and not self.photo:
            result = urllib.urlretrieve(self.url)
            self.photo.save(
                    os.path.basename(self.url),
                    File(open(result[0], 'rb'))
                    )
            self.save()

否则您的文件将在第一个0x1A字节处被截断。

Just a little remark. tvon answer works but, if you’re working on windows, you probably want to open() the file with 'rb'. Like this:

class CachedImage(models.Model):
    url = models.CharField(max_length=255, unique=True)
    photo = models.ImageField(upload_to=photo_path, blank=True)

    def cache(self):
        """Store image locally if we have a URL"""

        if self.url and not self.photo:
            result = urllib.urlretrieve(self.url)
            self.photo.save(
                    os.path.basename(self.url),
                    File(open(result[0], 'rb'))
                    )
            self.save()

or you’ll get your file truncated at the first 0x1A byte.


回答 3

这是一种效果很好的方法,它还允许您将文件转换为某种格式(以避免“无法将模式P编写为JPEG”错误):

import urllib2
from django.core.files.base import ContentFile
from PIL import Image
from StringIO import StringIO

def download_image(name, image, url):
    input_file = StringIO(urllib2.urlopen(url).read())
    output_file = StringIO()
    img = Image.open(input_file)
    if img.mode != "RGB":
        img = img.convert("RGB")
    img.save(output_file, "JPEG")
    image.save(name+".jpg", ContentFile(output_file.getvalue()), save=False)

其中image是django ImageField或your_model_instance.image,这里是一个用法示例:

p = ProfilePhoto(user=user)
download_image(str(user.id), p.image, image_url)
p.save()

希望这可以帮助

Here is a method that works well and allows you to convert the file to a certain format as well (to avoid “cannot write mode P as JPEG” error):

import urllib2
from django.core.files.base import ContentFile
from PIL import Image
from StringIO import StringIO

def download_image(name, image, url):
    input_file = StringIO(urllib2.urlopen(url).read())
    output_file = StringIO()
    img = Image.open(input_file)
    if img.mode != "RGB":
        img = img.convert("RGB")
    img.save(output_file, "JPEG")
    image.save(name+".jpg", ContentFile(output_file.getvalue()), save=False)

where image is the django ImageField or your_model_instance.image here is a usage example:

p = ProfilePhoto(user=user)
download_image(str(user.id), p.image, image_url)
p.save()

Hope this helps


回答 4

好的,如果您需要做的只是将现有图像文件路径与ImageField相关联,那么此解决方案可能会有所帮助:

from django.core.files.base import ContentFile

with open('/path/to/already/existing/file') as f:
  data = f.read()

# obj.image is the ImageField
obj.image.save('imgfilename.jpg', ContentFile(data))

好吧,如果认真的话,已经存在的图像文件将不会与ImageField关联,但是该文件的副本将在upload_to dir中创建为“ imgfilename.jpg”,并将与ImageField关联。

Ok, If all you need to do is associate the already existing image file path with the ImageField, then this solution may be helpfull:

from django.core.files.base import ContentFile

with open('/path/to/already/existing/file') as f:
  data = f.read()

# obj.image is the ImageField
obj.image.save('imgfilename.jpg', ContentFile(data))

Well, if be earnest, the already existing image file will not be associated with the ImageField, but the copy of this file will be created in upload_to dir as ‘imgfilename.jpg’ and will be associated with the ImageField.


回答 5

我所做的是创建自己的存储,该存储不会将文件保存到磁盘:

from django.core.files.storage import FileSystemStorage

class CustomStorage(FileSystemStorage):

    def _open(self, name, mode='rb'):
        return File(open(self.path(name), mode))

    def _save(self, name, content):
        # here, you should implement how the file is to be saved
        # like on other machines or something, and return the name of the file.
        # In our case, we just return the name, and disable any kind of save
        return name

    def get_available_name(self, name):
        return name

然后,在我的模型中,对于我的ImageField,我使用了新的自定义存储:

from custom_storage import CustomStorage

custom_store = CustomStorage()

class Image(models.Model):
    thumb = models.ImageField(storage=custom_store, upload_to='/some/path')

What I did was to create my own storage that will just not save the file to the disk:

from django.core.files.storage import FileSystemStorage

class CustomStorage(FileSystemStorage):

    def _open(self, name, mode='rb'):
        return File(open(self.path(name), mode))

    def _save(self, name, content):
        # here, you should implement how the file is to be saved
        # like on other machines or something, and return the name of the file.
        # In our case, we just return the name, and disable any kind of save
        return name

    def get_available_name(self, name):
        return name

Then, in my models, for my ImageField, I’ve used the new custom storage:

from custom_storage import CustomStorage

custom_store = CustomStorage()

class Image(models.Model):
    thumb = models.ImageField(storage=custom_store, upload_to='/some/path')

回答 6

如果您只想“设置”实际的文件名,而又不会导致加载和重新保存文件(!!)或使用charfield(!!!)的开销,那么您可能想尝试这样的方法- —

model_instance.myfile = model_instance.myfile.field.attr_class(model_instance, model_instance.myfile.field, 'my-filename.jpg')

它将照亮您的model_instance.myfile.url及其所有其他内容,就像您实际上已上传文件一样。

就像@ t-stone所说的,我们真正想要的是能够设置instance.myfile.path =’my-filename.jpg’,但是Django目前不支持。

If you want to just “set” the actual filename, without incurring the overhead of loading and re-saving the file (!!), or resorting to using a charfield (!!!), you might want to try something like this —

model_instance.myfile = model_instance.myfile.field.attr_class(model_instance, model_instance.myfile.field, 'my-filename.jpg')

This will light up your model_instance.myfile.url and all the rest of them just as if you’d actually uploaded the file.

Like @t-stone says, what we really want, is to be able to set instance.myfile.path = ‘my-filename.jpg’, but Django doesn’t currently support that.


回答 7

我认为这是最简单的解决方案:

from django.core.files import File

with open('path_to_file', 'r') as f:   # use 'rb' mode for python3
    data = File(f)
    model.image.save('filename', data, True)

THe simplest solution in my opinion:

from django.core.files import File

with open('path_to_file', 'r') as f:   # use 'rb' mode for python3
    data = File(f)
    model.image.save('filename', data, True)

回答 8

这些答案很多都已经过时了,我花了很多时间感到沮丧(对于Django和Web开发人员来说,我一般都是新手)。但是,我通过@iambibhas找到了这个出色的要点:https ://gist.github.com/iambibhas/5051911

import requests

from django.core.files import File
from django.core.files.temp import NamedTemporaryFile


def save_image_from_url(model, url):
    r = requests.get(url)

    img_temp = NamedTemporaryFile(delete=True)
    img_temp.write(r.content)
    img_temp.flush()

    model.image.save("image.jpg", File(img_temp), save=True)

A lot of these answers were outdated, and I spent many hours in frustration (I’m fairly new to Django & web dev in general). However, I found this excellent gist by @iambibhas: https://gist.github.com/iambibhas/5051911

import requests

from django.core.files import File
from django.core.files.temp import NamedTemporaryFile


def save_image_from_url(model, url):
    r = requests.get(url)

    img_temp = NamedTemporaryFile(delete=True)
    img_temp.write(r.content)
    img_temp.flush()

    model.image.save("image.jpg", File(img_temp), save=True)


回答 9

这可能不是您要寻找的答案。但是您可以使用charfield而不是ImageFile来存储文件的路径。这样,您可以以编程方式将上载的图像与字段相关联,而无需重新创建文件。

This is might not be the answer you are looking for. but you can use charfield to store the path of the file instead of ImageFile. In that way you can programmatically associate uploaded image to field without recreating the file.


回答 10

你可以试试:

model.ImageField.path = os.path.join('/Upload', generated_image_path)

You can try:

model.ImageField.path = os.path.join('/Upload', generated_image_path)

回答 11

class tweet_photos(models.Model):
upload_path='absolute path'
image=models.ImageField(upload_to=upload_path)
image_url = models.URLField(null=True, blank=True)
def save(self, *args, **kwargs):
    if self.image_url:
        import urllib, os
        from urlparse import urlparse
        file_save_dir = self.upload_path
        filename = urlparse(self.image_url).path.split('/')[-1]
        urllib.urlretrieve(self.image_url, os.path.join(file_save_dir, filename))
        self.image = os.path.join(file_save_dir, filename)
        self.image_url = ''
    super(tweet_photos, self).save()
class tweet_photos(models.Model):
upload_path='absolute path'
image=models.ImageField(upload_to=upload_path)
image_url = models.URLField(null=True, blank=True)
def save(self, *args, **kwargs):
    if self.image_url:
        import urllib, os
        from urlparse import urlparse
        file_save_dir = self.upload_path
        filename = urlparse(self.image_url).path.split('/')[-1]
        urllib.urlretrieve(self.image_url, os.path.join(file_save_dir, filename))
        self.image = os.path.join(file_save_dir, filename)
        self.image_url = ''
    super(tweet_photos, self).save()

回答 12

class Pin(models.Model):
    """Pin Class"""
    image_link = models.CharField(max_length=255, null=True, blank=True)
    image = models.ImageField(upload_to='images/', blank=True)
    title = models.CharField(max_length=255, null=True, blank=True)
    source_name = models.CharField(max_length=255, null=True, blank=True)
    source_link = models.CharField(max_length=255, null=True, blank=True)
    description = models.TextField(null=True, blank=True)
    tags = models.ForeignKey(Tag, blank=True, null=True)

    def __unicode__(self):
        """Unicode class."""
        return unicode(self.image_link)

    def save(self, *args, **kwargs):
        """Store image locally if we have a URL"""
        if self.image_link and not self.image:
            result = urllib.urlretrieve(self.image_link)
            self.image.save(os.path.basename(self.image_link), File(open(result[0], 'r')))
            self.save()
            super(Pin, self).save()
class Pin(models.Model):
    """Pin Class"""
    image_link = models.CharField(max_length=255, null=True, blank=True)
    image = models.ImageField(upload_to='images/', blank=True)
    title = models.CharField(max_length=255, null=True, blank=True)
    source_name = models.CharField(max_length=255, null=True, blank=True)
    source_link = models.CharField(max_length=255, null=True, blank=True)
    description = models.TextField(null=True, blank=True)
    tags = models.ForeignKey(Tag, blank=True, null=True)

    def __unicode__(self):
        """Unicode class."""
        return unicode(self.image_link)

    def save(self, *args, **kwargs):
        """Store image locally if we have a URL"""
        if self.image_link and not self.image:
            result = urllib.urlretrieve(self.image_link)
            self.image.save(os.path.basename(self.image_link), File(open(result[0], 'r')))
            self.save()
            super(Pin, self).save()

回答 13

加工!您可以使用FileSystemStorage保存图像。检查下面的例子

def upload_pic(request):
if request.method == 'POST' and request.FILES['photo']:
    photo = request.FILES['photo']
    name = request.FILES['photo'].name
    fs = FileSystemStorage()
##### you can update file saving location too by adding line below #####
    fs.base_location = fs.base_location+'/company_coverphotos'
##################
    filename = fs.save(name, photo)
    uploaded_file_url = fs.url(filename)+'/company_coverphotos'
    Profile.objects.filter(user=request.user).update(photo=photo)

Working! You can save image by using FileSystemStorage. check the example below

def upload_pic(request):
if request.method == 'POST' and request.FILES['photo']:
    photo = request.FILES['photo']
    name = request.FILES['photo'].name
    fs = FileSystemStorage()
##### you can update file saving location too by adding line below #####
    fs.base_location = fs.base_location+'/company_coverphotos'
##################
    filename = fs.save(name, photo)
    uploaded_file_url = fs.url(filename)+'/company_coverphotos'
    Profile.objects.filter(user=request.user).update(photo=photo)

回答 14

您可以使用Django REST框架和python Requests库以编程方式将图像保存到Django ImageField

这是一个例子:

import requests


def upload_image():
    # PATH TO DJANGO REST API
    url = "http://127.0.0.1:8080/api/gallery/"

    # MODEL FIELDS DATA
    data = {'first_name': "Rajiv", 'last_name': "Sharma"}

    #  UPLOAD FILES THROUGH REST API
    photo = open('/path/to/photo'), 'rb')
    resume = open('/path/to/resume'), 'rb')
    files = {'photo': photo, 'resume': resume}

    request = requests.post(url, data=data, files=files)
    print(request.status_code, request.reason) 

Your can use Django REST framework and python Requests library to Programmatically saving image to Django ImageField

Here is a Example:

import requests


def upload_image():
    # PATH TO DJANGO REST API
    url = "http://127.0.0.1:8080/api/gallery/"

    # MODEL FIELDS DATA
    data = {'first_name': "Rajiv", 'last_name': "Sharma"}

    #  UPLOAD FILES THROUGH REST API
    photo = open('/path/to/photo'), 'rb')
    resume = open('/path/to/resume'), 'rb')
    files = {'photo': photo, 'resume': resume}

    request = requests.post(url, data=data, files=files)
    print(request.status_code, request.reason) 

回答 15

在Django 3中,具有这样的模型:

class Item(models.Model):
   name = models.CharField(max_length=255, unique=True)
   photo= models.ImageField(upload_to='image_folder/', blank=True)

如果图片已经上传,我们可以直接做:

Item.objects.filter(...).update(photo='image_folder/sample_photo.png')

要么

my_item = Item.objects.get(id=5)
my_item.photo='image_folder/sample_photo.png'
my_item.save()

With Django 3, with a model such as this one:

class Item(models.Model):
   name = models.CharField(max_length=255, unique=True)
   photo= models.ImageField(upload_to='image_folder/', blank=True)

if the image has already been uploaded, we can directly do :

Item.objects.filter(...).update(photo='image_folder/sample_photo.png')

or

my_item = Item.objects.get(id=5)
my_item.photo='image_folder/sample_photo.png'
my_item.save()

如何使用Django的ORM提取随机记录?

问题:如何使用Django的ORM提取随机记录?

我有一个模型,代表我在网站上展示的绘画。在主要网页上,我想展示其中的一些:最新的,大多数时间未访问的网页,最受欢迎的网页和随机的网页。

我正在使用Django 1.0.2。

尽管使用django模型可以轻松提取其中的前3个,但最后一个(随机)会给我带来一些麻烦。在我看来,我可以将代码进行如下编码:

number_of_records = models.Painting.objects.count()
random_index = int(random.random()*number_of_records)+1
random_paint = models.Painting.get(pk = random_index)

在我看来,这看起来并不像我想要的东西-这完全是数据库抽象的一部分,应该包含在模型中。另外,在这里,我需要处理已删除的记录(然后所有记录的数量将无法覆盖所有可能的键值)以及可能还有很多其他事情。

我还有其他方法可以做,最好是在模型抽象内进行?

I have a model that represents paintings I present on my site. On the main webpage I’d like to show some of them: newest, one that was not visited for most time, most popular one and a random one.

I’m using Django 1.0.2.

While first 3 of them are easy to pull using django models, last one (random) causes me some trouble. I can ofc code it in my view, to something like this:

number_of_records = models.Painting.objects.count()
random_index = int(random.random()*number_of_records)+1
random_paint = models.Painting.get(pk = random_index)

It doesn’t look like something I’d like to have in my view tho – this is entirely part of database abstraction and should be in the model. Also, here I need to take care of removed records (then number of all records won’t cover me all the possible key values) and probably lots of other things.

Any other options how I can do it, preferably somehow inside the model abstraction?


回答 0

使用order_by('?')将在生产的第二天杀死数据库服务器。更好的方法类似于“从关系数据库获取随机行”中所述

from django.db.models.aggregates import Count
from random import randint

class PaintingManager(models.Manager):
    def random(self):
        count = self.aggregate(count=Count('id'))['count']
        random_index = randint(0, count - 1)
        return self.all()[random_index]

Using order_by('?') will kill the db server on the second day in production. A better way is something like what is described in Getting a random row from a relational database.

from django.db.models.aggregates import Count
from random import randint

class PaintingManager(models.Manager):
    def random(self):
        count = self.aggregate(count=Count('id'))['count']
        random_index = randint(0, count - 1)
        return self.all()[random_index]

回答 1

只需使用:

MyModel.objects.order_by('?').first()

它记录在QuerySet API中

Simply use:

MyModel.objects.order_by('?').first()

It is documented in QuerySet API.


回答 2

如果使用MySQL(即使不了解其他数据库),即使对于中型表,order_by(’?’)[:N]的解决方案也非常慢。

order_by('?')[:N]将被翻译为SELECT ... FROM ... WHERE ... ORDER BY RAND() LIMIT N查询。

这意味着将对表中的每一行执行RAND()函数,然后将根据该函数的值对整个表进行排序,然后将返回前N条记录。如果您的桌子很小,那很好。但是在大多数情况下,这是一个非常慢的查询。

我写了一个简单的函数,即使id有孔(某些行已删除),该函数也可以工作:

def get_random_item(model, max_id=None):
    if max_id is None:
        max_id = model.objects.aggregate(Max('id')).values()[0]
    min_id = math.ceil(max_id*random.random())
    return model.objects.filter(id__gte=min_id)[0]

在几乎所有情况下,它都比order_by(’?’)快。

The solutions with order_by(‘?’)[:N] are extremely slow even for medium-sized tables if you use MySQL (don’t know about other databases).

order_by('?')[:N] will be translated to SELECT ... FROM ... WHERE ... ORDER BY RAND() LIMIT N query.

It means that for every row in table the RAND() function will be executed, then the whole table will be sorted according to value of this function and then first N records will be returned. If your tables are small, this is fine. But in most cases this is a very slow query.

I wrote simple function that works even if id’s have holes (some rows where deleted):

def get_random_item(model, max_id=None):
    if max_id is None:
        max_id = model.objects.aggregate(Max('id')).values()[0]
    min_id = math.ceil(max_id*random.random())
    return model.objects.filter(id__gte=min_id)[0]

It is faster than order_by(‘?’) in almost all cases.


回答 3

这是一个简单的解决方案:

from random import randint

count = Model.objects.count()
random_object = Model.objects.all()[randint(0, count - 1)] #single random object

Here’s a simple solution:

from random import randint

count = Model.objects.count()
random_object = Model.objects.all()[randint(0, count - 1)] #single random object

回答 4

您可以在模型上创建一个经理来执行此类操作。首先要明白一个经理是什么,Painting.objects方法是包含一个管理者all()filter()get(),等创建自己的管理器允许您预先筛选结果,并拥有所有这些相同的方法,以及您自己的自定义方法,工作的结果。

编辑:我修改了代码以反映该order_by['?']方法。注意,管理器返回无限数量的随机模型。因此,我加入了一些用法代码来展示如何仅获得一个模型。

from django.db import models

class RandomManager(models.Manager):
    def get_query_set(self):
        return super(RandomManager, self).get_query_set().order_by('?')

class Painting(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

    objects = models.Manager() # The default manager.
    randoms = RandomManager() # The random-specific manager.

用法

random_painting = Painting.randoms.all()[0]

最后,您可以在模型上拥有许多经理,因此可以随意创建LeastViewsManager()MostPopularManager()

You could create a manager on your model to do this sort of thing. To first understand what a manager is, the Painting.objects method is a manager that contains all(), filter(), get(), etc. Creating your own manager allows you to pre-filter results and have all these same methods, as well as your own custom methods, work on the results.

EDIT: I modified my code to reflect the order_by['?'] method. Note that the manager returns an unlimited number of random models. Because of this I’ve included a bit of usage code to show how to get just a single model.

from django.db import models

class RandomManager(models.Manager):
    def get_query_set(self):
        return super(RandomManager, self).get_query_set().order_by('?')

class Painting(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

    objects = models.Manager() # The default manager.
    randoms = RandomManager() # The random-specific manager.

Usage

random_painting = Painting.randoms.all()[0]

Lastly, you can have many managers on your models, so feel free to create a LeastViewsManager() or MostPopularManager().


回答 5

其他答案可能很慢(使用order_by('?')),或者使用多个SQL查询。这是一个示例解决方案,没有排序,只有一个查询(假设Postgres):

random_instance_or_none = Model.objects.raw('''
    select * from {0} limit 1
    offset floor(random() * (select count(*) from {0}))
'''.format(Model._meta.db_table)).first()

请注意,如果表为空,这将引发索引错误。为自己编写一个与模型无关的辅助函数以进行检查。

The other answers are either potentially slow (using order_by('?')) or use more than one SQL query. Here’s a sample solution with no ordering and just one query (assuming Postgres):

random_instance_or_none = Model.objects.raw('''
    select * from {0} limit 1
    offset floor(random() * (select count(*) from {0}))
'''.format(Model._meta.db_table)).first()

Be aware that this will raise an index error if the table is empty. Write yourself a model-agnostic helper function to check for that.


回答 6

只是一个简单的想法,我该怎么做:

def _get_random_service(self, professional):
    services = Service.objects.filter(professional=professional)
    i = randint(0, services.count()-1)
    return services[i]

Just a simple idea how I do it:

def _get_random_service(self, professional):
    services = Service.objects.filter(professional=professional)
    i = randint(0, services.count()-1)
    return services[i]

回答 7

嗨,我需要从查询集中选择一个随机记录,该记录的长度我也需要报告(即,网页生成了描述项,并且记录还剩下)

q = Entity.objects.filter(attribute_value='this or that')
item_count = q.count()
random_item = q[random.randomint(1,item_count+1)]

花费了一半的时间(0.7s和1.7s):

item_count = q.count()
random_item = random.choice(q)

我猜想它可以避免在选择随机条目之前拉低整个查询,并使我的系统对于足以重复访问某个页面的页面有足够的响应能力,以使用户希望减少item_count的计数。

Hi I needed to select a random record from a queryset who’s length I also needed to report (ie web page produced described item and said records left)

q = Entity.objects.filter(attribute_value='this or that')
item_count = q.count()
random_item = q[random.randomint(1,item_count+1)]

took half as long(0.7s vs 1.7s) as:

item_count = q.count()
random_item = random.choice(q)

I’m guessing it avoids pulling down the whole query before selecting the random entry and made my system responsive enough for a page that is accessed repeatedly for a repetitive task where users want to see the item_count count down.


回答 8

DB中的随机化在python中令人讨厌和更好。但是同时,将所有数据从数据库带到python内存只是忽略大多数结果(尤其是在生产环境中)并不是一个好主意。我们可能还需要某种过滤。

  1. 所以基本上我们在DB有数据,
  2. 我们想使用python的rand函数
  3. 后记会从DB提供所有必需的数据。

基本上,使用2个查询比在DB CPU中随机选择(在DB中计算)或加载整个数据(繁重的网络利用率)要便宜得多。解释的解决方案必须具有可伸缩性,因此尝试在此处进行计划将不适用于特别是带有过滤器,软/硬删除甚至带有is_public标志的生产环境。因为我们生成的随机ID可能会从数据库中删除或在过滤器中被删减。假定max_id(records)== count(records)是一个坏习惯。

(当然,如果您不删除与查询使用的数据相当的百分比,或者您不想使用任何种类的过滤器,并且如果您有信心,可以使用random id,然后可以使用random)

如果您只想要一项。请参阅(@Valter Silva)

import random

mgr = models.Painting.objects
qs = mgr.filter(...)
random_id = random.choice(1, qs.count())-1        # <--- [ First Query Hit ]

random_paint = qs[random_id] ## <-- [ Second Query Hit ]

如果您想要n个项目。

import random

req_no_of_random_items = 8        ## i need 8 random items.
qs = models.Painting.objects.filter(...)

## if u prefer to use random values often, you can keep this in cache. 
possible_ids = list(qs.values_list('id', flat=True))        # <--- [ First Query Hit ]

possible_ids = random.choices(possible_ids, k=8)
random_paint = qs.filter(pk__in=possible_ids) ## in a generic case to get 'n' items.

或者,如果您想为生产使用更优化的代码,请使用缓存功能获取产品ID:

from django.core.cache import cache

def id_set_cache(qs):
    key = "some_random_key_for_cache"
    id_set =  cache.get(key)
    if id_set is None:
        id_set = list(qs.values_list('id', flat=True)
        cache.set(key, id_set)
    retrun id_set

Randomization in DB feels nasty and better in python. But at the same time, it’s not a good idea to bring all the data from DB to python memory just to ignore most of the results (especially in the production environment). we might need some sort of filtering also.

  1. So Basically we have data at DB,
  2. we wanna use the rand function of python
  3. and afterwords bring up the whole required data from DB.

Basically using 2 queries will be much less expensive than picking random in DB CPU (computing in DB) or loading whole data (heavy Network Utilization). Solutions explained must need a scalable nature trying to plan here won’t work for a production environment espicially with filters, soft/hard deletes, or even with an is_public flag. because probably random id we generated might be deleted from the database or will be cut down in filters. Its a bad practice to assume max_id(records) == count(records).

(Ofcouce, If you do’not delete a percentage of data which is comparable to query uses, or if you dont wanna use any kond of filters, and if you are confident, random id which you can proceed with a random )

if you want only one items. Refer ( @Valter Silva )

import random

mgr = models.Painting.objects
qs = mgr.filter(...)
random_id = random.choice(1, qs.count())-1        # <--- [ First Query Hit ]

random_paint = qs[random_id] ## <-- [ Second Query Hit ]

if you want ‘n’ items.

import random

req_no_of_random_items = 8        ## i need 8 random items.
qs = models.Painting.objects.filter(...)

## if u prefer to use random values often, you can keep this in cache. 
possible_ids = list(qs.values_list('id', flat=True))        # <--- [ First Query Hit ]

possible_ids = random.choices(possible_ids, k=8)
random_paint = qs.filter(pk__in=possible_ids) ## in a generic case to get 'n' items.

or if you want to have a more optimized code for production, use a cachefunction to get ids of products:

from django.core.cache import cache

def id_set_cache(qs):
    key = "some_random_key_for_cache"
    id_set =  cache.get(key)
    if id_set is None:
        id_set = list(qs.values_list('id', flat=True)
        cache.set(key, id_set)
    retrun id_set

回答 9

仅需注意(一种非常常见的)特殊情况,如果表中有一个索引自动递增列且没有删除,那么执行随机选择的最佳方法是查询,例如:

SELECT * FROM table WHERE id = RAND() LIMIT 1

假设有一个名为id的表列。在Django中,您可以通过以下方式进行操作:

Painting.objects.raw('SELECT * FROM appname_painting WHERE id = RAND() LIMIT 1')

其中必须用应用程序名称替换appname。

通常,使用id列,可以使用以下命令更快地完成order_by(’?’):

Paiting.objects.raw(
        'SELECT * FROM auth_user WHERE id>=RAND() * (SELECT MAX(id) FROM auth_user) LIMIT %d' 
    % needed_count)

Just to note a (fairly common) special case, if there is a indexed auto-increment column in the table with no deletes, the optimum way to do a random select is a query like:

SELECT * FROM table WHERE id = RAND() LIMIT 1

that assumes such a column named id for table. In django you can do this by:

Painting.objects.raw('SELECT * FROM appname_painting WHERE id = RAND() LIMIT 1')

in which you must replace appname with your application name.

In General, with an id column, the order_by(‘?’) can be done much faster with:

Paiting.objects.raw(
        'SELECT * FROM auth_user WHERE id>=RAND() * (SELECT MAX(id) FROM auth_user) LIMIT %d' 
    % needed_count)

回答 10

强烈建议从关系数据库中获取随机行

因为使用django orm这样的事情,如果您的数据表很大,会使数据库服务器特别生气:

解决方案是提供一个模型管理器并手动编写SQL查询;)

更新

无需编写custom即可在任何数据库后端(甚至是非依赖数据库)上运行的另一种解决方案ModelManager在Django中从查询集获取随机对象

This is Highly recomended Getting a random row from a relational database

Because using django orm to do such a thing like that, will makes your db server angry specially if you have big data table :|

And the solution is provide a Model Manager and write the SQL query by hand ;)

Update:

Another solution which works on any database backend even non-rel ones without writing custom ModelManager. Getting Random objects from a Queryset in Django


回答 11

您可能想要使用对任何迭代器进行抽样相同的方法,尤其是如果您打算对多个项目进行抽样以创建样本集时,尤其如此。@MatijnPieters和@DzinX为此投入了很多思考:

def random_sampling(qs, N=1):
    """Sample any iterable (like a Django QuerySet) to retrieve N random elements

    Arguments:
      qs (iterable): Any iterable (like a Django QuerySet)
      N (int): Number of samples to retrieve at random from the iterable

    References:
      @DZinX:  https://stackoverflow.com/a/12583436/623735
      @MartinPieters: https://stackoverflow.com/a/12581484/623735
    """
    samples = []
    iterator = iter(qs)
    # Get the first `N` elements and put them in your results list to preallocate memory
    try:
        for _ in xrange(N):
            samples.append(iterator.next())
    except StopIteration:
        raise ValueError("N, the number of reuested samples, is larger than the length of the iterable.")
    random.shuffle(samples)  # Randomize your list of N objects
    # Now replace each element by a truly random sample
    for i, v in enumerate(qs, N):
        r = random.randint(0, i)
        if r < N:
            samples[r] = v  # at a decreasing rate, replace random items
    return samples

You may want to use the same approach that you’d use to sample any iterator, especially if you plan to sample multiple items to create a sample set. @MatijnPieters and @DzinX put a lot of thought into this:

def random_sampling(qs, N=1):
    """Sample any iterable (like a Django QuerySet) to retrieve N random elements

    Arguments:
      qs (iterable): Any iterable (like a Django QuerySet)
      N (int): Number of samples to retrieve at random from the iterable

    References:
      @DZinX:  https://stackoverflow.com/a/12583436/623735
      @MartinPieters: https://stackoverflow.com/a/12581484/623735
    """
    samples = []
    iterator = iter(qs)
    # Get the first `N` elements and put them in your results list to preallocate memory
    try:
        for _ in xrange(N):
            samples.append(iterator.next())
    except StopIteration:
        raise ValueError("N, the number of reuested samples, is larger than the length of the iterable.")
    random.shuffle(samples)  # Randomize your list of N objects
    # Now replace each element by a truly random sample
    for i, v in enumerate(qs, N):
        r = random.randint(0, i)
        if r < N:
            samples[r] = v  # at a decreasing rate, replace random items
    return samples

回答 12

一种更简单的方法包括简单地过滤到感兴趣的记录集,并根据random.sample需要选择尽可能多的记录集:

from myapp.models import MyModel
import random

my_queryset = MyModel.objects.filter(criteria=True)  # Returns a QuerySet
my_object = random.sample(my_queryset, 1)  # get a single random element from my_queryset
my_objects = random.sample(my_queryset, 5)  # get five random elements from my_queryset

注意,您应该有一些代码来验证它my_queryset是否为空。如果第一个参数包含的元素太少,则random.sample返回ValueError: sample larger than population

One much easier approach to this involves simply filtering down to the recordset of interest and using random.sample to select as many as you want:

from myapp.models import MyModel
import random

my_queryset = MyModel.objects.filter(criteria=True)  # Returns a QuerySet
my_object = random.sample(my_queryset, 1)  # get a single random element from my_queryset
my_objects = random.sample(my_queryset, 5)  # get five random elements from my_queryset

Note that you should have some code in place to verify that my_queryset is not empty; random.sample returns ValueError: sample larger than population if the first argument contains too few elements.


回答 13

自动删除不删除主键的方法

如果您有一个表,其中主键是一个没有间隔的连续整数,那么以下方法应该有效:

import random
max_id = MyModel.objects.last().id
random_id = random.randint(0, max_id)
random_obj = MyModel.objects.get(pk=random_id)

与遍历表的所有行的其他方法相比,此方法效率更高。尽管它确实需要两个数据库查询,但两者都很简单。此外,它很简单,不需要定义任何额外的类。但是,它的适用性仅限于具有自动递增主键的表,其中行从未删除,因此id序列中没有空格。

在删除行(例如空格)的情况下,如果重试该方法直到随机选择一个现有的主键,该方法仍然可以使用。

参考文献

Method for auto-incrementing primary key with no deletes

If you have a table where the primary key is a sequential integer with no gaps, then the following method should work:

import random
max_id = MyModel.objects.last().id
random_id = random.randint(0, max_id)
random_obj = MyModel.objects.get(pk=random_id)

This method is much more efficient than other methods here that iterate through all rows of the table. While it does require two database queries, both are trivial. Furthermore, it’s simple and doesn’t require defining any extra classes. However, it’s applicability is limited to tables with an auto-incrementing primary key where rows have never deleted, such that there are no gaps in the sequence of ids.

In the case where rows have been deleted such that are gaps, this method could still work if it is retried until an existing primary key is randomly selected.

References


回答 14

我有一个非常简单的解决方案,使自定义经理:

class RandomManager(models.Manager):
    def random(self):
        return random.choice(self.all())

然后添加模型:

class Example(models.Model):
    name = models.CharField(max_length=128)
    objects = RandomManager()

现在,您可以使用它:

Example.objects.random()

I got very simple solution, make custom manager:

class RandomManager(models.Manager):
    def random(self):
        return random.choice(self.all())

and then add in model:

class Example(models.Model):
    name = models.CharField(max_length=128)
    objects = RandomManager()

Now, you can use it:

Example.objects.random()

如何在Django中按日期范围过滤查询对象?

问题:如何在Django中按日期范围过滤查询对象?

我在一个模型中有一个领域,例如:

class Sample(models.Model):
    date = fields.DateField(auto_now=False)

现在,我需要按日期范围过滤对象。

如何过滤日期在1-Jan-2011和之间的所有对象31-Jan-2011

I’ve got a field in one model like:

class Sample(models.Model):
    date = fields.DateField(auto_now=False)

Now, I need to filter the objects by a date range.

How do I filter all the objects that have a date between 1-Jan-2011 and 31-Jan-2011?


回答 0

Sample.objects.filter(date__range=["2011-01-01", "2011-01-31"])

或者,如果您只是想按月过滤:

Sample.objects.filter(date__year='2011', 
                      date__month='01')

编辑

正如伯恩哈德Vallant说,如果你想查询集去掉了specified range ends,你应该考虑自己的解决方案,它采用GT / LT(大于/小于号)。

Use

Sample.objects.filter(date__range=["2011-01-01", "2011-01-31"])

Or if you are just trying to filter month wise:

Sample.objects.filter(date__year='2011', 
                      date__month='01')

Edit

As Bernhard Vallant said, if you want a queryset which excludes the specified range ends you should consider his solution, which utilizes gt/lt (greater-than/less-than).


回答 1

您可以对对象使用djangofilterdatetime.date

import datetime
samples = Sample.objects.filter(sampledate__gte=datetime.date(2011, 1, 1),
                                sampledate__lte=datetime.date(2011, 1, 31))

You can use django’s filter with datetime.date objects:

import datetime
samples = Sample.objects.filter(sampledate__gte=datetime.date(2011, 1, 1),
                                sampledate__lte=datetime.date(2011, 1, 31))

回答 2

使用过滤器进行Django范围设置时,请确保您知道使用日期对象与日期时间对象之间的区别。__range是日期中包含的内容,但是如果您使用datetime对象作为结束日期,则如果未设置时间,它将不包括该天的条目。

    startdate = date.today()
    enddate = startdate + timedelta(days=6)
    Sample.objects.filter(date__range=[startdate, enddate])

返回从开始日期到结束日期的所有条目,包括那些日期的条目。不好的例子,因为这将在未来一周返回条目,但您会遇到麻烦。

    startdate = datetime.today()
    enddate = startdate + timedelta(days=6)
    Sample.objects.filter(date__range=[startdate, enddate])

根据日期字段的设置时间,将缺少24小时的输入值。

When doing django ranges with a filter make sure you know the difference between using a date object vs a datetime object. __range is inclusive on dates but if you use a datetime object for the end date it will not include the entries for that day if the time is not set.

    startdate = date.today()
    enddate = startdate + timedelta(days=6)
    Sample.objects.filter(date__range=[startdate, enddate])

returns all entries from startdate to enddate including entries on those dates. Bad example since this is returning entries a week into the future, but you get the drift.

    startdate = datetime.today()
    enddate = startdate + timedelta(days=6)
    Sample.objects.filter(date__range=[startdate, enddate])

will be missing 24 hours worth of entries depending on what the time for the date fields is set to.


回答 3

通过使用datetime.timedelta在范围中的最后一个日期添加日期,可以避免由于DateTimeField/date对象比较精度不足而导致的“阻抗不匹配”(如果使用范围,则可能发生)。其工作原理如下:

start = date(2012, 12, 11)
end = date(2012, 12, 18)
new_end = end + datetime.timedelta(days=1)

ExampleModel.objects.filter(some_datetime_field__range=[start, new_end])

如前所述,如果不这样做,记录将在最后一天被忽略。

进行编辑以避免使用datetime.combine-与a进行比较时,坚持日期实例似乎更合乎逻辑DateTimeField,而不是乱扔掉(且容易混淆)datetime对象。请参阅下面的注释中的进一步说明。

You can get around the “impedance mismatch” caused by the lack of precision in the DateTimeField/date object comparison — that can occur if using range — by using a datetime.timedelta to add a day to last date in the range. This works like:

start = date(2012, 12, 11)
end = date(2012, 12, 18)
new_end = end + datetime.timedelta(days=1)

ExampleModel.objects.filter(some_datetime_field__range=[start, new_end])

As discussed previously, without doing something like this, records are ignored on the last day.

Edited to avoid the use of datetime.combine — seems more logical to stick with date instances when comparing against a DateTimeField, instead of messing about with throwaway (and confusing) datetime objects. See further explanation in comments below.


回答 4

很简单

YourModel.objects.filter(YOUR_DATE_FIELD__date=timezone.now())

为我工作

Is simple,

YourModel.objects.filter(YOUR_DATE_FIELD__date=timezone.now())

Works for me


回答 5

为了使其更加灵活,可以设计如下的FilterBackend:

class AnalyticsFilterBackend(generic_filters.BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        predicate = request.query_params # or request.data for POST

        if predicate.get('from_date', None) is not None and predicate.get('to_date', None) is not None:
            queryset = queryset.filter(your_date__range=(predicate['from_date'], predicate['to_date']))

        if predicate.get('from_date', None) is not None and predicate.get('to_date', None) is None:
            queryset = queryset.filter(your_date__gte=predicate['from_date'])

        if predicate.get('to_date', None) is not None and predicate.get('from_date', None) is None:
            queryset = queryset.filter(your_date__lte=predicate['to_date'])
        return queryset

To make it more flexible, you can design a FilterBackend as below:

class AnalyticsFilterBackend(generic_filters.BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        predicate = request.query_params # or request.data for POST

        if predicate.get('from_date', None) is not None and predicate.get('to_date', None) is not None:
            queryset = queryset.filter(your_date__range=(predicate['from_date'], predicate['to_date']))

        if predicate.get('from_date', None) is not None and predicate.get('to_date', None) is None:
            queryset = queryset.filter(your_date__gte=predicate['from_date'])

        if predicate.get('to_date', None) is not None and predicate.get('from_date', None) is None:
            queryset = queryset.filter(your_date__lte=predicate['to_date'])
        return queryset

回答 6

今天仍然有意义。您也可以这样做:

import dateutil
import pytz

date = dateutil.parser.parse('02/11/2019').replace(tzinfo=pytz.UTC)

Still relevant today. You can also do:

import dateutil
import pytz

date = dateutil.parser.parse('02/11/2019').replace(tzinfo=pytz.UTC)

Django:显示选择值

问题:Django:显示选择值

models.py:

class Person(models.Model):
    name = models.CharField(max_length=200)
    CATEGORY_CHOICES = (
        ('M', 'Male'),
        ('F', 'Female'),
    )
    gender = models.CharField(max_length=200, choices=CATEGORY_CHOICES)
    to_be_listed = models.BooleanField(default=True)
    description = models.CharField(max_length=20000, blank=True)

views.py:

def index(request):
    latest_person_list2 = Person.objects.filter(to_be_listed=True)
    return object_list(request, template_name='polls/schol.html',
                       queryset=latest_person_list, paginate_by=5)

在模板上,当我调用时person.gender,我得到'M'or 'F'而不是'Male'or 'Female'

如何显示值('Male''Female')而不是代码('M'/ 'F')?

models.py:

class Person(models.Model):
    name = models.CharField(max_length=200)
    CATEGORY_CHOICES = (
        ('M', 'Male'),
        ('F', 'Female'),
    )
    gender = models.CharField(max_length=200, choices=CATEGORY_CHOICES)
    to_be_listed = models.BooleanField(default=True)
    description = models.CharField(max_length=20000, blank=True)

views.py:

def index(request):
    latest_person_list2 = Person.objects.filter(to_be_listed=True)
    return object_list(request, template_name='polls/schol.html',
                       queryset=latest_person_list, paginate_by=5)

On the template, when I call person.gender, I get 'M' or 'F' instead of 'Male' or 'Female'.

How to display the value ('Male' or 'Female') instead of the code ('M'/'F')?


回答 0

看来您处在正确的轨道上- get_FOO_display()无疑是您想要的:

模板中,您不包括()方法名称。请执行下列操作:

{{ person.get_gender_display }}

It looks like you were on the right track – get_FOO_display() is most certainly what you want:

In templates, you don’t include () in the name of a method. Do the following:

{{ person.get_gender_display }}

回答 1

对于每个设置了选项的字段,该对象将具有get_FOO_display()方法,其中FOO是字段的名称。此方法返回该字段的“人类可读”值。

在视图中

person = Person.objects.filter(to_be_listed=True)
context['gender'] = person.get_gender_display()

在模板中

{{ person.get_gender_display }}

get_FOO_display()的文档

For every field that has choices set, the object will have a get_FOO_display() method, where FOO is the name of the field. This method returns the “human-readable” value of the field.

In Views

person = Person.objects.filter(to_be_listed=True)
context['gender'] = person.get_gender_display()

In Template

{{ person.get_gender_display }}

Documentation of get_FOO_display()


回答 2

其他人指出,您需要的是get_FOO_display方法。我正在使用这个:

def get_type(self):
    return [i[1] for i in Item._meta.get_field('type').choices if i[0] == self.type][0]

遍历特定项目的所有选择,直到找到与项目类型匹配的项目

Others have pointed out that a get_FOO_display method is what you need. I’m using this:

def get_type(self):
    return [i[1] for i in Item._meta.get_field('type').choices if i[0] == self.type][0]

which iterates over all of the choices that a particular item has until it finds the one that matches the items type


Django设置默认表单值

问题:Django设置默认表单值

我有一个模型如下:

class TankJournal(models.Model):
    user = models.ForeignKey(User)
    tank = models.ForeignKey(TankProfile)
    ts = models.IntegerField(max_length=15)
    title = models.CharField(max_length=50)
    body = models.TextField()

我也有上述模型的模型形式,如下所示:

class JournalForm(ModelForm):
    tank = forms.IntegerField(widget=forms.HiddenInput()) 

    class Meta:
        model = TankJournal
        exclude = ('user','ts')

我想知道如何为该坦克隐藏字段设置默认值。这是我到目前为止显示/保存表格的功能:

def addJournal(request, id=0):
    if not request.user.is_authenticated():
        return HttpResponseRedirect('/')

    # checking if they own the tank
    from django.contrib.auth.models import User
    user = User.objects.get(pk=request.session['id'])

    if request.method == 'POST':
        form = JournalForm(request.POST)
        if form.is_valid():
            obj = form.save(commit=False)

            # setting the user and ts
            from time import time
            obj.ts = int(time())
            obj.user = user

            obj.tank = TankProfile.objects.get(pk=form.cleaned_data['tank_id'])

            # saving the test
            obj.save()

    else:
        form = JournalForm()

    try:
        tank = TankProfile.objects.get(user=user, id=id)
    except TankProfile.DoesNotExist:
        return HttpResponseRedirect('/error/')

I have a Model as follows:

class TankJournal(models.Model):
    user = models.ForeignKey(User)
    tank = models.ForeignKey(TankProfile)
    ts = models.IntegerField(max_length=15)
    title = models.CharField(max_length=50)
    body = models.TextField()

I also have a model form for the above model as follows:

class JournalForm(ModelForm):
    tank = forms.IntegerField(widget=forms.HiddenInput()) 

    class Meta:
        model = TankJournal
        exclude = ('user','ts')

I want to know how to set the default value for that tank hidden field. Here is my function to show/save the form so far:

def addJournal(request, id=0):
    if not request.user.is_authenticated():
        return HttpResponseRedirect('/')

    # checking if they own the tank
    from django.contrib.auth.models import User
    user = User.objects.get(pk=request.session['id'])

    if request.method == 'POST':
        form = JournalForm(request.POST)
        if form.is_valid():
            obj = form.save(commit=False)

            # setting the user and ts
            from time import time
            obj.ts = int(time())
            obj.user = user

            obj.tank = TankProfile.objects.get(pk=form.cleaned_data['tank_id'])

            # saving the test
            obj.save()

    else:
        form = JournalForm()

    try:
        tank = TankProfile.objects.get(user=user, id=id)
    except TankProfile.DoesNotExist:
        return HttpResponseRedirect('/error/')

回答 0

您可以使用初始被解释这里

您有两个选择,可以在调用表单构造函数时填充值:

form = JournalForm(initial={'tank': 123})

或在表单定义中设置值:

tank = forms.IntegerField(widget=forms.HiddenInput(), initial=123) 

You can use initial which is explained here

You have two options either populate the value when calling form constructor:

form = JournalForm(initial={'tank': 123})

or set the value in the form definition:

tank = forms.IntegerField(widget=forms.HiddenInput(), initial=123) 

回答 1

其他解决方案:创建表单后设置初始:

form.fields['tank'].initial = 123

Other solution: Set initial after creating the form:

form.fields['tank'].initial = 123

回答 2

如果要通过POST值创建模型形式,则可以通过以下方式分配初始值:

form = SomeModelForm(request.POST, initial={"option": "10"})

https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/#providing-initial-values

If you are creating modelform from POST values initial can be assigned this way:

form = SomeModelForm(request.POST, initial={"option": "10"})

https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/#providing-initial-values


回答 3

我有另一个解决方案(如果其他人正在使用该模型中的以下方法,我会发布它):

class onlyUserIsActiveField(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(onlyUserIsActiveField, self).__init__(*args, **kwargs)
        self.fields['is_active'].initial = False

    class Meta:
        model = User
        fields = ['is_active']
        labels = {'is_active': 'Is Active'}
        widgets = {
            'is_active': forms.CheckboxInput( attrs={
                            'class':          'form-control bootstrap-switch',
                            'data-size':      'mini',
                            'data-on-color':  'success',
                            'data-on-text':   'Active',
                            'data-off-color': 'danger',
                            'data-off-text':  'Inactive',
                            'name':           'is_active',

            })
        }

缩写在__init__函数上定义为self.fields['is_active'].initial = False

I had this other solution (I’m posting it in case someone else as me is using the following method from the model):

class onlyUserIsActiveField(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(onlyUserIsActiveField, self).__init__(*args, **kwargs)
        self.fields['is_active'].initial = False

    class Meta:
        model = User
        fields = ['is_active']
        labels = {'is_active': 'Is Active'}
        widgets = {
            'is_active': forms.CheckboxInput( attrs={
                            'class':          'form-control bootstrap-switch',
                            'data-size':      'mini',
                            'data-on-color':  'success',
                            'data-on-text':   'Active',
                            'data-off-color': 'danger',
                            'data-off-text':  'Inactive',
                            'name':           'is_active',

            })
        }

The initial is definded on the __init__ function as self.fields['is_active'].initial = False


回答 4

我希望这可以帮助您:

form.instance.updatedby = form.cleaned_data['updatedby'] = request.user.id

I hope this can help you:

form.instance.updatedby = form.cleaned_data['updatedby'] = request.user.id

回答 5

Django文档所述initial不是default

  • 字段的初始值打算在HTML中显示。但是,如果用户删除该值,并最终为此字段发送回空白值,则该initial值将丢失。因此,您不会获得默认行为所期望的结果。

  • 默认行为是:如果值验证过程将采取data说法不包含字段的任何值。

要实现这一点,一种简单的方法是将initial和结合起来clean_<field>()

class JournalForm(ModelForm):
    tank = forms.IntegerField(widget=forms.HiddenInput(), initial=123) 

    (...)

    def clean_tank(self):
        if not self['tank'].html_name in self.data:
            return self.fields['tank'].initial
        return self.cleaned_data['tank']

As explained in Django docs, initial is not default.

  • The initial value of a field is intended to be displayed in an HTML . But if the user delete this value, and finally send back a blank value for this field, the initial value is lost. So you do not obtain what is expected by a default behaviour.

  • The default behaviour is : the value that validation process will take if data argument do not contain any value for the field.

To implement that, a straightforward way is to combine initial and clean_<field>():

class JournalForm(ModelForm):
    tank = forms.IntegerField(widget=forms.HiddenInput(), initial=123) 

    (...)

    def clean_tank(self):
        if not self['tank'].html_name in self.data:
            return self.fields['tank'].initial
        return self.cleaned_data['tank']