জ্যাঙ্গোতে নালার অনুমতি দেয় এমন অনন্য ক্ষেত্র


135

আমার কাছে মডেল ফু আছে যা ফিল্ড বার রয়েছে। বার ক্ষেত্রটি অনন্য হওয়া উচিত, তবে এতে নালার অনুমতি দিন, অর্থাত্ বার ক্ষেত্রটি যদি আমি একাধিক রেকর্ডের অনুমতি দিতে চাই nullতবে তা যদি nullমান না হয় তবে অবশ্যই তা অনন্য।

এখানে আমার মডেল:

class Foo(models.Model):
    name = models.CharField(max_length=40)
    bar = models.CharField(max_length=40, unique=True, blank=True, null=True, default=None)

এবং এখানে টেবিলের জন্য সম্পর্কিত এসকিউএল রয়েছে:

CREATE TABLE appl_foo
(
    id serial NOT NULL,
     "name" character varying(40) NOT NULL,
    bar character varying(40),
    CONSTRAINT appl_foo_pkey PRIMARY KEY (id),
    CONSTRAINT appl_foo_bar_key UNIQUE (bar)
)   

অ্যাডমিন ইন্টারফেসটি যখন 1 টিরও বেশি আইও অবজেক্ট তৈরি করতে ব্যবহার করে যেখানে বারটি শূন্য থাকে এটি আমাকে ত্রুটি দেয়: "এই বারের সাথে ফুও ইতিমধ্যে বিদ্যমান।"

তবে আমি যখন ডাটাবেজে intoোকি (পোস্টগ্রিএসকিউএল):

insert into appl_foo ("name", bar) values ('test1', null)
insert into appl_foo ("name", bar) values ('test2', null)

এটি কাজ করে, ঠিক আছে, বারটি নাল হওয়ায় এটি আমাকে 1 টিরও বেশি রেকর্ড সন্নিবেশ করতে দেয়, তাই ডাটাবেস আমাকে যা করতে চায় তা করতে দেয়, এটি জাঙ্গো মডেলের কিছু ভুল। কোন ধারনা?

সম্পাদনা

ডিবি পর্যন্ত সমাধানের বহনযোগ্যতা কোনও সমস্যা নয়, আমরা পোস্টগ্র্রেসে খুশি। আমি কলযোগ্য হিসাবে অনন্য স্থাপনের চেষ্টা করেছি, যা বারের নির্দিষ্ট মানগুলির জন্য সত্য / মিথ্যা ফিরিয়ে দেওয়া আমার ফাংশন ছিল , এটি কোনও ত্রুটি দেয় নি, তবে এটির মত প্রভাব ছিল না যেমন এর কোনও ফল ছিল না।

এখনও অবধি, আমি বারের সম্পত্তি থেকে অনন্য স্পেসিফায়ারটিকে সরিয়ে দিয়েছি এবং অ্যাপ্লিকেশনটিতে বারের স্বতন্ত্রতা পরিচালনা করছি , তবে এখনও আরও মার্জিত সমাধানের সন্ধান করছি। কোন সুপারিশ?


আমি এখনও মন্তব্য করতে পারি না তাই এখানে শক্তিশালী কিছুটা সংযোজন: যেহেতু জাজানো 1.4 আপনার def get_db_prep_value(self, value, connection, prepared=False)পদ্ধতি কল হিসাবে প্রয়োজন হবে । পরীক্ষা করে দেখুন groups.google.com/d/msg/django-users/Z_AXgg2GCqs/zKEsfu33OZMJ আরও তথ্য জন্য। নিম্নলিখিত পদ্ধতিটি আমার জন্যও কাজ করে: Def get_prep_value (স্ব, মান): যদি মান == "": #if Jjango '' স্ট্রিং সংরক্ষণ করার চেষ্টা করে, db কিছুই না প্রেরণ করুন (NULL) আর কিছুই নয়: ফেরতের মান # অন্যদিকে, কেবল মানটি পাস করুন
জেনস

আমি এর জন্য একটি জ্যাঙ্গোর টিকিট খুলেছি। আপনার সমর্থন যোগ করুন। Code.djangoproject.com/ticket/30210#ticket
কার্ল ব্রুবেকার

উত্তর:


154

টিকিট # 9039 নির্ধারিত হওয়ার পরে জাজঙ্গো স্বতন্ত্রতা পরীক্ষার উদ্দেশ্যে NULL কে NULL এর সমতুল্য বলে বিবেচনা করেননি, দেখুন:

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

এখানে সমস্যাটি হ'ল চরফিল্ডের একটি ফর্মের জন্য সাধারণ "ফাঁকা" মানটি খালি স্ট্রিং, কোনওটি নয়। সুতরাং আপনি যদি ক্ষেত্রটি ফাঁকা ছেড়ে দেন, আপনি ডিবিতে সঞ্চিত NULL নয়, একটি খালি স্ট্রিং পাবেন। জ্যাঙ্গো এবং ডাটাবেস উভয় নিয়মের অধীনে খালি স্ট্রিংগুলি স্বতন্ত্রতা পরীক্ষার জন্য খালি স্ট্রিংয়ের সমান।

আপনি অ্যাডমিন ইন্টারফেসকে একটি ফাঁকা স্ট্রিংয়ের জন্য NUL সঞ্চয় করতে বাধ্য করতে পারেন একটি পরিষ্কার_বার পদ্ধতি দিয়ে ফাঁকের জন্য আপনার নিজস্ব কাস্টমাইজড মডেল ফর্ম সরবরাহ করে যা খালি স্ট্রিংটিকে কোনওটিতে রূপান্তরিত করে না:

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

2
যদি বারটি ফাঁকা থাকে তবে এটিকে প্রাক-সংরক্ষণ পদ্ধতিতে কোনওটি দিয়ে প্রতিস্থাপন করুন। আমি মনে করি কোডটি আরও শুকনো হবে।
আশীষ গুপ্ত

6
এই উত্তরটি কেবল ফর্ম-ভিত্তিক ডেটা ইনপুট জন্য সহায়তা করে, কিন্তু আসলে ডেটা অখণ্ডতা রক্ষা করতে কিছুই করে না। শেল থেকে, কোনও এপিআই বা অন্য কোনও মাধ্যমে আমদানি স্ক্রিপ্টগুলির মাধ্যমে ডেটা প্রবেশ করা যেতে পারে। ডেটা স্পর্শ করতে পারে এমন প্রতিটি ফর্মের জন্য কাস্টম কেস তৈরির চেয়ে সেভ () পদ্ধতিটি ওভাররাইড করা আরও ভাল।
শ্যাকার

জ্যাঙ্গো ১.৯++ এর ক্ষেত্রে একটি fieldsবা excludeঅ্যাট্রিবিউট দরকার ModelFormMetaঅ্যাডমিনে ব্যবহারের জন্য মডেলফর্ম থেকে অভ্যন্তরীণ শ্রেণিকে বাদ দিয়ে আপনি এটিকে ঘিরে কাজ করতে পারেন । রেফারেন্স: ডকস.ডজ্যাঙ্গোপ্রজেক্ট.com
en

62

** সম্পাদনা 11/30/2015 : অজগর 3 এ, মডিউল-গ্লোবাল __metaclass__ভেরিয়েবল আর সমর্থিত নয় । Additionaly, হিসাবে বর্গ ছিল অবচিত :Django 1.10SubfieldBase

ডক্স থেকে :

django.db.models.fields.subclassing.SubfieldBaseঅবচিত করা হয়েছে এবং জ্যাঙ্গো ১.১০ এ সরানো হবে। Orতিহাসিকভাবে, এটি ক্ষেত্রগুলি পরিচালনা করতে ব্যবহৃত হয়েছিল যেখানে ডাটাবেস থেকে লোড করার সময় ধরণের রূপান্তর প্রয়োজন ছিল, তবে এটি .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

আমি মনে করি অ্যাডমিনের ক্লিনড_ডাটা ওভাররাইড করার চেয়ে আরও ভাল উপায় হ'ল চারফিল্ড সাবক্লাস করা - এইভাবে ক্ষেত্রটি যেভাবেই ফর্ম অ্যাক্সেস করে না কেন, এটি "কেবলমাত্র কাজ করবে"। ''এটি ডাটাবেসে প্রেরণের ঠিক আগেই ধরতে পারেন এবং এটি ডাটাবেস থেকে বেরিয়ে আসার ঠিক পরেই এনএইউএল ধরতে পারেন, এবং বাকি জ্যাঙ্গো জানেন না / যত্ন করবে না। একটি দ্রুত এবং নোংরা উদাহরণ:

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ফাইলে ফেলেছি যা আমার সাইটের মূলের মধ্যে থাকে, তারপরে আমি কেবল from mysite.extras import CharNullFieldআমার অ্যাপ্লিকেশনটির models.pyফাইলটিতেই পারি। ক্ষেত্রটি কেবল চারফিল্ডের মতো কাজ করে - blank=True, null=Trueক্ষেত্রটি ঘোষণার সময় সেট করতে হবে মনে রাখবেন , নাহলে জ্যাঙ্গো একটি বৈধতা ত্রুটি (ক্ষেত্রের প্রয়োজন) ফেলে দেবে বা এমন একটি ডিবি কলাম তৈরি করবে যা NULL গ্রহণ করবে না।


3
get_prep_value এ, মানটির অনেকগুলি স্থান থাকলে আপনার মানটি কেটে ফেলা উচিত।
ax003d

1
আপডেট হওয়া উত্তর এখানে জ্যাঙ্গো 1.10 এর সাথে ইমেলফিল্ড ব্যবহার করে 2016 সালে ভাল কাজ করে works
k0nG

4
আপনি যদি একটি CharFieldহতে আপডেট করে থাকেন তবে আপনাকে CharNullFieldএটি তিনটি ধাপে করা দরকার। প্রথমে null=Trueক্ষেত্রটি যুক্ত করুন এবং তা স্থানান্তর করুন। তারপরে, কোনও ফাঁকা মান আপডেট করার জন্য ডেটা মাইগ্রেশন করুন যাতে সেগুলি নাল হয়। অবশেষে ক্ষেত্রটিকে চারনুলফিল্ডে রূপান্তর করুন। আপনি ডেটা মাইগ্রেশন করার আগে আপনি যদি ক্ষেত্রটি রূপান্তর করেন তবে আপনার ডেটা মাইগ্রেশন কিছুই করবে না।
mlissner

3
নোট করুন যে আপডেট হওয়া সমাধানে, from_db_value()অতিরিক্ত contexপ্যারামিটারটি থাকা উচিত নয় । এটি হওয়া উচিতdef from_db_value(self, value, expression, connection):
ফিল গাইফোর্ড 15'18

1
@ ফিলগাইফোর্ডের মন্তব্যটি 2.0 হিসাবে প্রযোজ্য।
শহীদ হক

16

কারণ আমি স্ট্যাকওভারফ্লোতে নতুন আমি এখনও উত্তরগুলির উত্তর দেওয়ার অনুমতি পাচ্ছি না, তবে আমি উল্লেখ করতে চাই যে দার্শনিক দৃষ্টিকোণ থেকে, আমি এই প্রশ্নের সর্বাধিক জনপ্রিয় উত্তরের সাথে একমত হতে পারি না। (ক্যারেন ট্রেসির দ্বারা)

ওপিটির জন্য তার বার ক্ষেত্রটি অনন্য হতে হবে যদি এর কোনও মান থাকে এবং অন্যথায় শূন্য হয়। তারপরে অবশ্যই এটি হওয়া উচিত যে মডেল নিজেই নিশ্চিত করে যে এটি কেস is এটি পরীক্ষা করার জন্য এটি বাহ্যিক কোডে রেখে দেওয়া যায় না, কারণ এর অর্থ এটি বাইপাস করা যায়। (অথবা আপনি ভবিষ্যতে নতুন দৃষ্টিভঙ্গি লিখেছেন কিনা তা পরীক্ষা করতে ভুলে যেতে পারেন)

অতএব, আপনার কোডটিকে সত্যই OOP রাখতে আপনার অবশ্যই নিজের ফু মডেলের অভ্যন্তরীণ পদ্ধতিটি ব্যবহার করতে হবে। সেভ () পদ্ধতি বা ক্ষেত্রটি পরিবর্তন করা ভাল বিকল্প, তবে এটি করার জন্য কোনও ফর্ম ব্যবহার করা অবশ্যই তা নয়।

ভবিষ্যতে আমি যে মডেলগুলি সংজ্ঞায়িত করতে পারি তার জন্য ব্যক্তিগতভাবে আমি প্রস্তাবিত চারনুলফিল্ডটি ব্যবহার করতে পছন্দ করি।


13

দ্রুত সমাধানটি হ'ল:

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

    if not self.bar:
        self.bar = None

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

2
সচেতন হোন যে MyModel.objects.bulk_create()ব্যবহারটি এই পদ্ধতিটিকে বাইপাস করবে।
বেনিয়ামিনগোল্ডার

আমরা যখন অ্যাডমিন প্যানেল থেকে সংরক্ষণ করি তখন কী এই পদ্ধতিটি ডাকা হবে? আমি চেষ্টা করেছি কিন্তু তা হয় নি।
কিশান মেহতা

1
@ কিশান জ্যাঙ্গো-অ্যাডমিন প্যানেল দুর্ভাগ্যবশত এই হুকগুলি এড়িয়ে
যাবেন

@ ই-সন্তুষ্ট আপনার যুক্তিটি যথাযথ তাই আমি এটি প্রয়োগ করেছি, তবে ত্রুটিটি এখনও একটি সমস্যা। আমি নালকে বলা হচ্ছে এটি একটি সদৃশ।
ভিনসেন্ট বাসকারেলো

6

আর একটি সম্ভাব্য সমাধান

class Foo(models.Model):
    value = models.CharField(max_length=255, unique=True)

class Bar(models.Model):
    foo = models.OneToOneField(Foo, null=True)

আপনি একটি অপ্রয়োজনীয় সম্পর্ক তৈরি করার পরে এটি কোনও ভাল সমাধান নয়।
বুর্ক Özdemir

3

এটি এখন ঠিক করা হয়েছে যে https://code.djangoproject.com/ticket/4136 সমাধান করা হয়েছে। জ্যাঙ্গো 1.11+ এ আপনি models.CharField(unique=True, null=True, blank=True)খালি মানগুলিতে ম্যানুয়ালি রূপান্তর না করে ব্যবহার করতে পারেন None


1

আমার সম্প্রতি একই চাহিদা ছিল। বিভিন্ন ক্ষেত্রের সাবক্লাসিংয়ের পরিবর্তে আমি আমার মডেলটিতে (নীচে 'মাইমোডেল' নামে) সেভ () মেটোডকে ওভাররাইড করা বেছে নিয়েছি:

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

আপনার যদি একটি মডেল মাইমোডেল থাকে এবং আমার_ফিল্ড নাল বা অনন্য হতে চান তবে আপনি মডেলের সংরক্ষণের পদ্ধতিটি ওভাররাইড করতে পারেন:

class MyModel(models.Model):
    my_field = models.TextField(unique=True, default=None, null=True, blank=True) 

    def save(self, **kwargs):
        self.my_field = self.my_field or None
        super().save(**kwargs)

এইভাবে, ক্ষেত্রটি ফাঁকা হতে পারে না কেবল খালি বা ফাঁকা হবে। নাল স্বতন্ত্রতার বিরোধিতা করে না


0

আরও ভাল বা খারাপের জন্য, জ্যাঙ্গো স্বতন্ত্রতা পরীক্ষার উদ্দেশ্যে NULLসমতুল্য বলে মনে করে NULL। আপনার নিজস্বতার স্বতন্ত্রতা যাচাইয়ের বাস্তবায়নের লেখার সংক্ষিপ্ততার কোনও উপায় নেই যা NULLএটি কোনও টেবিলে যতবারই ঘটে না কেন এটি অনন্য হিসাবে বিবেচনা করে।

(এবং মনে রাখবেন যে কিছু ডিবি সমাধানগুলি একই দৃষ্টিভঙ্গি গ্রহণ করে NULL, তাই কোনও ডিবির ধারণাগুলির উপর নির্ভর করে কোড NULLঅন্যের কাছে পোর্টেবল নাও হতে পারে)


6
এটি সঠিক উত্তর নয়। ব্যাখ্যার জন্য এই উত্তরটি দেখুন ।
কার্ল জি

2
এটি সঠিক নয় বলে সম্মত হয়েছেন। আমি জাজানো ১.৪-এ কেবল ইন্টিজারফিল্ড (ফাঁকা = সত্য, নাল = সত্য, অনন্য = সত্য) পরীক্ষা করেছি এবং এটি নাল মান সহ একাধিক সারিকে অনুমতি দেয়।
অস্পষ্টতা

0

আপনি এই UniqueConstraintশর্তটি তালিকায় nullable_field=nullঅন্তর্ভুক্ত না করার শর্তের সাথে যুক্ত করতে পারেন fields। আপনার যদি nullable_fieldউইচ মানটিও সীমাবদ্ধতার প্রয়োজন হয় nullতবে আপনি অতিরিক্তটি যুক্ত করতে পারেন।

দ্রষ্টব্য: জাজো ২.২ থেকে ইউনিক কনস্ট্রেন্টটি যুক্ত করা হয়েছিল

class Foo(models.Model):
    name = models.CharField(max_length=40)
    bar = models.CharField(max_length=40, unique=True, blank=True, null=True, default=None)
    
    class Meta:
        constraints = [
            # For bar == null only
            models.UniqueConstraint(fields=['name'], name='unique__name__when__bar__null',
                                    condition=Q(bar__isnull=True)),
            # For bar != null only
            models.UniqueConstraint(fields=['name', 'bar'], name='unique__name__when__bar__not_null')
        ]
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.