वहाँ 2 क्षेत्रों में एक अद्वितीय आईडी बनाने के लिए एक रास्ता है?


14

यहाँ मेरा मॉडल है:

class GroupedModels(models.Model):
    other_model_one = models.ForeignKey('app.other_model')
    other_model_two = models.ForeignKey('app.other_model')

अनिवार्य रूप से, जो मैं चाहता हूं वह other_modelइस तालिका में अद्वितीय होने के लिए है। इसका मतलब है कि अगर कोई रिकॉर्ड है जहां other_model_oneआईडी है 123, तो मुझे other_model_twoआईडी के साथ एक और रिकॉर्ड बनाने की अनुमति नहीं देनी चाहिए123 । मुझे cleanलगता है कि मैं ओवरराइड कर सकता हूं, लेकिन मैं सोच रहा था कि क्या django में कुछ बनाया गया है।

मैं PSQL के साथ 2.2.5 संस्करण का उपयोग कर रहा हूं।

संपादित करें: यह एक unqiue एक साथ स्थिति नहीं है। यदि मैं other_model_one_id=1और अन्य के साथ एक रिकॉर्ड जोड़ता हूं other_model_two_id=2, तो मुझे other_model_one_id=2और अन्य के साथ एक और रिकॉर्ड जोड़ने में सक्षम नहीं होना चाहिएother_model_two_id=1


Django संस्करण क्या आप उपयोग कर रहे हैं?
विलेम वैन ओन्सेम

मैं संस्करण २.२.५ का उपयोग कर रहा हूं
१३:५.5 पर पिटफुल


1
यह एक अद्वितीय स्थिति नहीं है, यह अद्वितीय है, लेकिन 2 क्षेत्रों में अगर यह किसी भी मायने में है।
पिटफुल

जवाबों:


10

मैं यहां कई विकल्पों की व्याख्या करता हूं, हो सकता है कि उनमें से एक या एक संयोजन आपके लिए उपयोगी हो।

अधिभावी save

आपका बाधा एक व्यावसायिक नियम है, आप saveडेटा को सुसंगत रखने के लिए विधि को ओवरराइड कर सकते हैं :


class GroupedModels(models.Model): 
    # ...
    def clean(self):
        if (self.other_model_one.pk == self.other_model_two.pk):
            raise ValidationError({'other_model_one':'Some message'}) 
        if (self.other_model_one.pk < self.other_model_two.pk):
            #switching models
            self.other_model_one, self.other_model_two = self.other_model_two, self.other_model_one
    # ...
    def save(self, *args, **kwargs):
        self.clean()
        super(GroupedModels, self).save(*args, **kwargs)

डिजाइन बदलें

मैंने समझने के लिए एक नमूना रखा। चलो इस परिदृश्य को मान लेते हैं:

class BasketballMatch(models.Model):
    local = models.ForeignKey('app.team')
    visitor = models.ForeignKey('app.team')

अब, आप एक टीम के साथ एक मैच खेलने से बचना चाहते हैं, टीम A केवल एक बार टीम B के साथ खेल सकती है (लगभग आपके नियम)। आप अपने मॉडलों को इस रूप में नया स्वरूप दे सकते हैं:

class BasketballMatch(models.Model):
    HOME = 'H'
    GUEST = 'G'
    ROLES = [
        (HOME, 'Home'),
        (GUEST, 'Guest'),
    ]
    match_id = models.IntegerField()
    role = models.CharField(max_length=1, choices=ROLES)
    player = models.ForeignKey('app.other_model')

    class Meta:
      unique_together = [ ( 'match_id', 'role', ) ,
                          ( 'match_id', 'player',) , ]

ManyToManyField.symmetrical

यह एक सममित मुद्दे की तरह दिखता है , django इसे आपके लिए संभाल सकता है। GroupedModelsमॉडल बनाने के बजाय , केवल अपने आप पर एक कईToManyField फ़ील्ड बनाएं OtherModel:

from django.db import models
class OtherModel(models.Model):
    ...
    grouped_models = models.ManyToManyField("self")

इन परिदृश्यों के लिए इसे django ने बनाया है।


दृष्टिकोण एक वह है जिसका मैं उपयोग कर रहा था (लेकिन एक डेटाबेस बाधा के लिए उम्मीद कर रहा था)। दृष्टिकोण 2 मेरे परिदृश्य में इसमें थोड़ा भिन्न है, अगर किसी टीम ने एक खेल खेला है, तो वे फिर कभी एक खेल नहीं खेल सकते हैं। मैंने एप्रोच 3 का उपयोग नहीं किया क्योंकि समूहीकरण के लिए मेरे पास अधिक डेटा था जिसे मैं स्टोर करना चाहता था। जवाब के लिए धन्यवाद।
पिटफॉल

यदि किसी टीम ने कोई खेल खेला है, तो वे फिर कभी एक खेल नहीं खेल सकते हैं। क्योंकि मैंने match_idटीमों को असीमित मैच खेलने की अनुमति देने के लिए एक जैसे अवरोधों को शामिल किया था। खेल को फिर से प्रतिबंधित करने के लिए इस फ़ील्ड को हटा दें।
दानी हेररा

आह येस! धन्यवाद, मैंने याद किया कि और मेरा अन्य मॉडल एक से एक फील्ड हो सकता है।
पिटफॉल

1
मुझे लगता है कि मुझे विकल्प नंबर 2 सबसे अच्छा लगता है। मेरे पास एकमात्र मुद्दा यह है कि यकीनन इसे "औसत" उपयोगकर्ता के लिए एक कस्टम रूप की आवश्यकता है, एक ऐसी दुनिया में जहां व्यवस्थापक को FE के रूप में उपयोग किया जाता है। दुर्भाग्य से, मैं उस दुनिया में रहता हूं। लेकिन मुझे लगता है कि यह स्वीकृत उत्तर होना चाहिए। धन्यवाद!
पिटफॉल

दूसरा विकल्प जाने का रास्ता है। यह एक बेहतरीन जवाब है। @Pitfall व्यवस्थापक के बारे में मैंने एक और उत्तर जोड़ा है। व्यवस्थापक प्रपत्र को हल करने के लिए एक बड़ी समस्या नहीं होनी चाहिए।
cezar

1

यह बहुत संतोषजनक जवाब नहीं है, लेकिन दुर्भाग्य से सच्चाई यह है कि जो आप एक साधारण अंतर्निहित सुविधा के साथ वर्णन कर रहे हैं, वह करने का कोई तरीका नहीं है।

आपने जो वर्णन किया है cleanवह काम करेगा, लेकिन आपको मैन्युअल रूप से कॉल करने के लिए सावधान रहना होगा क्योंकि मुझे लगता है कि यह केवल मॉडलफार्म का उपयोग करते समय स्वचालित रूप से कॉल किया जाता है। आप में सक्षम हो सकता है एक जटिल डेटाबेस बाधा बनाने में लेकिन यह Django के बाहर रहता है और आपको डेटाबेस अपवादों को संभालना होगा (जो लेन-देन के बीच में Django में मुश्किल हो सकता है)।

शायद डेटा को संरचित करने का एक बेहतर तरीका है?


हां, आप सही हैं कि इसे मैन्युअल रूप से कहा जाना है यही कारण है कि मुझे दृष्टिकोण पसंद नहीं आया। यह केवल उसी तरह काम करता है जैसा मैं एडमिन में चाहता हूं, जैसा कि आपने उल्लेख किया है।
पिटफॉल

0

दानी हेरेरा का पहले से ही एक शानदार जवाब है , हालांकि मैं इसके बारे में विस्तार से बताना चाहता हूं।

जैसा कि दूसरे विकल्प में बताया गया है, ओपी द्वारा आवश्यक समाधान डिजाइन को बदलने और दो अद्वितीय बाधाओं जोड़ी को लागू करने के लिए है। बास्केटबॉल मैचों के साथ समानता समस्या को बहुत ही व्यावहारिक तरीके से दर्शाती है।

एक बास्केटबॉल मैच के बजाय, मैं फुटबॉल (या सॉकर) गेम के साथ उदाहरण का उपयोग करता हूं। एक फुटबॉल गेम (जिसे मैं इसे कहता हूं Event) दो टीमों द्वारा खेला जाता है (मेरे मॉडल में एक टीम है Competitor)। यह कई-से-कई संबंध ( m:n) हैn इस विशेष मामले में दो तक सीमित होने के , सिद्धांत असीमित संख्या के लिए उपयुक्त है।

यहां बताया गया है कि हमारे मॉडल कैसे दिखते हैं:

class Competitor(models.Model):
    name = models.CharField(max_length=100)
    city = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Event(models.Model):
    title = models.CharField(max_length=200)
    venue = models.CharField(max_length=100)
    time = models.DateTimeField()
    participants = models.ManyToManyField(Competitor)

    def __str__(self):
        return self.title

एक घटना हो सकती है:

  • शीर्षक: कारबाओ कप, चौथा दौर,
  • स्थल: एनफील्ड
  • समय: 30. अक्टूबर 2019, 19:30 जीएमटी
  • प्रतिभागियों:
    • नाम: लिवरपूल, शहर: लिवरपूल
    • नाम: शस्त्रागार, शहर: लंदन

अब हमें प्रश्न से मुद्दे को हल करना होगा। Django स्वचालित रूप से कई-कई संबंधों के साथ मॉडल के बीच एक मध्यवर्ती तालिका बनाता है, लेकिन हम एक कस्टम मॉडल का उपयोग कर सकते हैं और आगे के फ़ील्ड जोड़ सकते हैं। मैं उस मॉडल को कॉल करता हूं Participant:

वर्ग प्रतिभागी (मॉडल। मॉडल):
    रोल्स = (
        ('एच', 'होम'),
        ('वी', 'आगंतुक'),
    )
    घटना = मॉडल। ForeignKey (घटना, on_delete = model.CASCADE)
    प्रतियोगी = मॉडल। ForeignKey (प्रतियोगी, on_delete = model.CASCADE)
    भूमिका = मॉडल.चार्फिल्ड (मैक्सिमम = 1, विकल्प = रोल)

    कक्षा मेटा:
        अनूठे_तो = (
            ('घटना', 'भूमिका'),
            ('घटना', 'प्रतियोगी'),
        )

    डीई __str __ (स्व):
        वापसी '{} - {}'। स्वरूप (स्वयंवर, स्व .get_role_display ())

ManyToManyFieldएक विकल्प होता है throughकि हम में मध्यवर्ती मॉडल निर्दिष्ट करने के लिए अनुमति देता है। आइए इसे मॉडल में बदलें Event:

class Event(models.Model):
    title = models.CharField(max_length=200)
    venue = models.CharField(max_length=100)
    time = models.DateTimeField()
    participants = models.ManyToManyField(
        Competitor,
        related_name='events', # if we want to retrieve events for a competitor
        through='Participant'
    )

    def __str__(self):
        return self.title

अद्वितीय बाधाएं अब प्रति ईवेंट प्रतियोगियों की संख्या को स्वचालित रूप से दो तक सीमित कर देंगी (क्योंकि केवल दो भूमिकाएं हैं: होम और विजिटर )।

किसी विशेष घटना (फुटबॉल खेल) में केवल एक घरेलू टीम और केवल एक आगंतुक टीम हो सकती है। एक क्लब ( Competitor) या तो घरेलू टीम के रूप में या आगंतुक टीम के रूप में दिखाई दे सकता है।

हम इन सभी चीजों को अब कैसे प्रबंधित करते हैं? ऐशे ही:

from django.contrib import admin

from .models import Competitor, Event, Participant


class ParticipantInline(admin.StackedInline): # or admin.TabularInline
    model = Participant
    max_num = 2


class CompetitorAdmin(admin.ModelAdmin):
    fields = ('name', 'city',)


class EventAdmin(admin.ModelAdmin):
    fields = ('title', 'venue', 'time',)
    inlines = [ParticipantInline]


admin.site.register(Competitor, CompetitorAdmin)
admin.site.register(Event, EventAdmin)

हमने Participantइनलाइन को इनलाइन के रूप में जोड़ा है EventAdmin। जब हम नया बनाते हैं तो हम Eventघरेलू टीम और आगंतुक टीम चुन सकते हैं। विकल्प max_numप्रविष्टियों की संख्या को 2 तक सीमित करता है, इसलिए प्रति घटना में 2 टीमों को नहीं जोड़ा जा सकता है।

यह एक अलग उपयोग के मामलों के लिए refactored जा सकता है। मान लीजिए कि हमारे कार्यक्रम तैराकी प्रतियोगिताओं हैं और घर और आगंतुक के बजाय, हमारे पास 1 से 8 तक गलियां हैं। हम सिर्फ Participant:

class Participant(models.Model):
    ROLES = (
        ('L1', 'lane 1'),
        ('L2', 'lane 2'),
        # ... L3 to L8
    )
    event = models.ForeignKey(Event, on_delete=models.CASCADE)
    competitor = models.ForeignKey(Competitor, on_delete=models.CASCADE)
    role = models.CharField(max_length=1, choices=ROLES)

    class Meta:
        unique_together = (
            ('event', 'role'),
            ('event', 'competitor'),
        )

    def __str__(self):
        return '{} - {}'.format(self.event, self.get_role_display())

इस संशोधन के साथ हमारे पास यह घटना हो सकती है:

  • शीर्षक: FINA 2019, 50 मीटर बैकस्ट्रोक पुरुषों का फाइनल,

    • आयोजन स्थल: नांबु यूनिवर्सिटी म्यूनिसिपल एक्वेटिक्स सेंटर
    • समय: 28. जुलाई 2019, 20:02 UTC + 9
    • प्रतिभागियों:

      • नाम: माइकल एंड्रयू, शहर: एडिना, यूएसए, भूमिका: लेन १
      • नाम: ज़ेन वडेल, शहर: ब्लोमफ़ोन्टिन, दक्षिण अफ्रीका, भूमिका: लेन २
      • नाम: एवगेनी रायलोव, शहर: नोवोट्रोित्सक, रूस, भूमिका: लेन ३
      • नाम: क्लेमेंट कोलेनिकोव, शहर: मास्को, रूस, भूमिका: लेन ४

      // और इतने पर लेन 5 से 8 लेन (स्रोत: विकिपीडिया

एक तैराक केवल एक बार गर्मी में दिखाई दे सकता है, और एक गर्मी में केवल एक बार कब्जा किया जा सकता है।

मैंने GitHub: https://github.com/cezar77/competition पर कोड डाला ।

फिर, सभी क्रेडिट दानी हेरेरा को जाते हैं। मुझे आशा है कि यह उत्तर पाठकों को कुछ अतिरिक्त मूल्य प्रदान करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.