Уникальные поля, которые позволяют, аннулируют в Django

121
задан Constantinius 3 July 2017 в 15:17
поделиться

5 ответов

Django has not considered NULL to be equal to NULL for the purpose of uniqueness checks since ticket #9039 was fixed, see:

http://code.djangoproject.com/ticket/9039

The issue here is that the normalized "blank" value for a form CharField is an empty string, not None. So if you leave the field blank, you get an empty string, not NULL, stored in the DB. Empty strings are equal to empty strings for uniqueness checks, under both Django and database rules.

You can force the admin interface to store NULL for an empty string by providing your own customized model form for Foo with a clean_bar method that turns the empty string into None:

class FooForm(forms.ModelForm):
    class Meta:
        model = Foo
    def clean_bar(self):
        return self.cleaned_data['bar'] or None

class FooAdmin(admin.ModelAdmin):
    form = FooForm
143
ответ дан 24 November 2019 в 01:31
поделиться

Что бы там ни было Django полагает NULL быть эквивалентным NULL в целях проверок уникальности. Нет действительно никакого пути вокруг этого за исключением записи Вашей собственной реализации проверки уникальности, которая полагает NULL быть уникальной, неважно, сколько раз это происходит в таблице.

(и имеют в виду, что некоторые решения для DB получают то же представление NULL, так код, полагающийся на идеи одного DB [приблизительно 114] не могут быть портативными другим)

0
ответ дан James Bennett 24 November 2019 в 01:31
поделиться

** редактировать 30.11.2015 : в Python 3 глобальная переменная модуля __ метакласс __ больше не поддерживается . Кроме того, с Django 1.10 класс SubfieldBase был объявлен устаревшим :

из документов :

django.db. models.fields.subclassing.SubfieldBase устарел и будет удален в Django 1.10. Исторически он использовался для обработки полей, где требовалось преобразование типа при загрузке из базы данных, но он не использовался в вызовах .values ​​() или в агрегатах. Он был заменен на from_db_value () . Обратите внимание, что новый подход не вызывает метод to_python () при назначении, как это было в случае с SubfieldBase .

Следовательно, как было предложено в from_db_value () документация и этот пример , это решение должно быть изменено на:

class CharNullField(models.CharField):

    """
    Subclass of the CharField that allows empty strings to be stored as NULL.
    """

    description = "CharField that stores NULL but returns ''."

    def from_db_value(self, value, expression, connection, contex):
        """
        Gets value right out of the db and changes it if its ``None``.
        """
        if value is None:
            return ''
        else:
            return value


    def to_python(self, value):
        """
        Gets value right out of the db or an instance, and changes it if its ``None``.
        """
        if isinstance(value, models.CharField):
            # If an instance, just return the instance.
            return value
        if value is None:
            # If db has NULL, convert it to ''.
            return ''

        # Otherwise, just return the value.
        return value

    def get_prep_value(self, value):
        """
        Catches value right before sending to db.
        """
        if value == '':
            # If Django tries to save an empty string, send the db None (NULL).
            return None
        else:
            # Otherwise, just pass the value.
            return value

Я думаю, что лучше, чем переопределение cleaned_data в админке, было бы создать подкласс charfield - таким образом, независимо от того, какая форма обращается к полю, оно будет «просто работать». Вы можете поймать '' непосредственно перед его отправкой в ​​базу данных и поймать NULL сразу после его выхода из базы данных, и остальная часть Django не будет знать / заботиться. Быстрый и грязный пример:

from django.db import models


class CharNullField(models.CharField):  # subclass the CharField
    description = "CharField that stores NULL but returns ''"
    __metaclass__ = models.SubfieldBase  # this ensures to_python will be called

    def to_python(self, value):
        # this is the value right out of the db, or an instance
        # if an instance, just return the instance
        if isinstance(value, models.CharField):
            return value 
        if value is None:  # if the db has a NULL (None in Python)
            return ''      # convert it into an empty string
        else:
            return value   # otherwise, just return the value

    def get_prep_value(self, value):  # catches value right before sending to db
        if value == '':   
            # if Django tries to save an empty string, send the db None (NULL)
            return None
        else:
            # otherwise, just pass the value
            return value  

Для своего проекта я скопировал это в файл extras.py , который находится в корне моего сайта, тогда я могу просто из mysite.extras импортировать CharNullField в файл models.py моего приложения. Поле действует так же, как CharField - просто не забудьте установить blank = True, null = True при объявлении поля, иначе Django выдаст ошибку проверки (поле обязательно) или создаст столбец db, который не ' t принимает NULL.

60
ответ дан 24 November 2019 в 01:31
поделиться

Недавно у меня было такое же требование. Вместо того, чтобы создавать подклассы для разных полей, я решил переопределить метод save () в моей модели (названной ниже «MyModel») следующим образом:

def save(self):
        """overriding save method so that we can save Null to database, instead of empty string (project requirement)"""
        # get a list of all model fields (i.e. self._meta.fields)...
        emptystringfields = [ field for field in self._meta.fields \
                # ...that are of type CharField or Textfield...
                if ((type(field) == django.db.models.fields.CharField) or (type(field) == django.db.models.fields.TextField)) \
                # ...and that contain the empty string
                and (getattr(self, field.name) == "") ]
        # set each of these fields to None (which tells Django to save Null)
        for field in emptystringfields:
            setattr(self, field.name, None)
        # call the super.save() method
        super(MyModel, self).save()    
1
ответ дан 24 November 2019 в 01:31
поделиться

Быстрое решение - сделать :

def save(self, *args, **kwargs):

    if not self.bar:
        self.bar = None

    super(Foo, self).save(*args, **kwargs)
13
ответ дан 24 November 2019 в 01:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: