2009-10-30 15 views
20

मैं गणना किए गए फ़ील्ड के साथ एक मॉडल बनाना चाहता हूं जिसे मैं सॉर्ट करना लागू कर सकता हूं। बी -django - गणना की गई फ़ील्ड द्वारा क्वेरीसेट को ऑर्डर करना

  1. डी = एक:

    class Foo(models.Model): 
        A = models.IntegerField(..) 
        B = models.IntegerField(..) 
        C = models.ForeignKey(..) 
    

    मैं एक डी और एक ई क्षेत्र है जो निम्न सूत्रों द्वारा गणना कर रहे हैं करना चाहते हैं: उदाहरण के लिए, मान लीजिए कि मैं निम्नलिखित मॉडल है चलो

  2. ई = एक - एक्स (जहाँ X मॉडल सी के प्रासंगिक रिकॉर्ड का एक क्षेत्र है)

को लागू करने के लिए इस तुच्छ हो सकता है अगर मैं छँटाई आवेदन करने की आवश्यकता नहीं किया था; मैं सिर्फ मॉडल वर्ग में गुण जोड़ता हूं। हालांकि, मुझे इन क्षेत्रों द्वारा आदेश देने की आवश्यकता है।

एक समाधान सभी रिकॉर्ड्स को स्मृति में लाने के लिए है और वहां सॉर्टिंग करना है, जिसे मैं आखिरी उपाय (यह पेजिनेशन के बारे में चीजों को तोड़ देगा)।

क्या मैं कोशिश कर रहा हूं कि हासिल करने का कोई तरीका है? किसी भी मार्गदर्शन की सराहना की है।

संपादित करें: denormalization एक नो-गो है। फ़ील्ड एक्स का मूल्य बहुत बार बदलता है और बहुत सारे फू रिकॉर्ड्स मॉडल सी के एक रिकॉर्ड से संबंधित होते हैं। एक्स के अपडेट के लिए ई

+1

बस का कहना है कि एक समान प्रश्न पूछा कर दिया गया है: यहाँ प्रश्नों आप नए छंटाई तकनीक के साथ के बारे में पूछा हैं http : //stackoverflow.com/questions/930865/how-to-sort-by-a-computed-value-in-django – cethegeek

उत्तर

15

पर हजारों अपडेट की आवश्यकता होगी, मैं क्वेरीज़ पर extra विधि पर एक नज़र डालेगा और order_by पैरामीटर निर्दिष्ट करें।

+0

जब तक डी और ई डेटाबेस में भौतिक नहीं होते हैं, तो मुझे नहीं लगता कि अतिरिक्त (order_by) मदद करने जा रहा है। – cethegeek

+0

@celopes sql का ऑर्डर ऑन द फ्लाई गणना फ़ील्ड के साथ काम करता है, इसलिए अतिरिक्त विधि यहां उपयुक्त है। – shanyu

+0

कृपया इसके लिए 'अतिरिक्त()' पर भरोसा न करें क्योंकि इसका बहिष्कार किया जाना है। –

1

मुझे वर्तमान में एक Django इंस्टॉल नहीं मिला है, लेकिन मुझे लगता है कि आप जो पूछ रहे हैं वह कस्टम सेव करना है, जैसे कि डी और ई स्वचालित रूप से जेनरेट होते हैं। मुझे नहीं पता कि यूनिकोड पर आपकी विदेशीकी की वापसी क्या है, इसलिए मुझे लगता है कि यह एक स्ट्रिंग नहीं है और पूर्णांक के लिए टोकन vlaue के रूप में "valueName" असाइन करना है जिसे आप उपयोग करना चाहते हैं।

भी हो, यह इस तरह एक सा जाना चाहिए: कि की अंतिम पंक्ति से पहले

class Foo(models.Model): 
    A = models.IntegerField(..) 
    B = models.IntegerField(..) 
    C = models.ForeignKey(..) 
    D = models.IntegerField(..) 
    E = models.IntegerField(..) 
    def save(self): 
     self.D = self.A - self.B 
     self.E = self.A - self.C.valueName 
     super(Foo, self).save() 

कुछ भी (सुपर()) पूर्व बचाने के लिए किया जाएगा, कुछ भी करने के बाद पोस्ट है। यह वास्तव में सबसे महत्वपूर्ण बिंदु है।

+1

यह ठीक है अगर shanyu डेटाबेस में गणना वाले फ़ील्ड को भौतिक बनाने में कोई फर्क नहीं पड़ता है। – cethegeek

+1

कृपया मेरा संपादन देखें, denormalization एक विकल्प नहीं है। सहायता के लिए धन्यवाद। – shanyu

27

आप कुछ तर्क duplicaton कोई फ़र्क नहीं पड़ेगा, तो निम्नलिखित काम करेगा:

Foo.objects.extra(select={'d_field': 'A - B'}).extra(order_by=['d_field']) 
+0

यदि आपको पैरामीटर पास करने की आवश्यकता है, तो आपको ऑर्डर्ड डिक्ट का उपयोग करना चाहिए: https://docs.djangoproject.com/en/1.8/ref/models/querysets/#extra – fjsj

+0

कृपया इसके लिए 'अतिरिक्त() 'पर भरोसा न करें इसका बहिष्कार किया जाना है। –

+1

'Foo.objects.extra (select = {'d_field': 'ए - बी'}, order_by = ['d_field'])' एक 'अतिरिक्त' पर्याप्त है – WeizhongTu

0

मुझे लगता है कि * args और बचाने के विधि में ** kwargs बिना, यह एक त्रुटि देता है। और जैसा कि सेलोप्स ने कहा, यह केवल एक समाधान है यदि आप डेटाबेस में गणना वाले क्षेत्र को भौतिक बनाने में कोई फर्क नहीं पड़ता है।

class Foo(models.Model): 
    A = models.IntegerField(..) 
    B = models.IntegerField(..) 
    C = models.ForeignKey(..) 
    D = models.IntegerField(..) 
    E = models.IntegerField(..) 

    def save(self, *args, **kwargs): 
     self.D = self.A - self.B 
     self.E = self.A - self.C.X 
     super(Foo, self).save(*args, **kwargs) 

    class Meta: 
     ordering = ["E", "D"] 
13

extra() का उपयोग कर के रूप में यह भविष्य में बहिष्कृत कर दिया करने के लिए होती है करने से बचें।

Django 1 के बाद से।7 आप इस

Foo.objects.annotate(ordering=F('A') - F('B')).order_by('ordering') 

वहाँ भी ungoing गए कार्य में प्राप्त करने के लिए annotate() और order_by() के संयोजन का उपयोग कर सकते हैं भाव सब ORM से अधिक इस्तेमाल किया जा करने की अनुमति तो निम्नलिखित Django के भविष्य के संस्करणों में काम करना चाहिए:

Foo.objects.order_by(F('A') - F('B')) 
4

Simon says के रूप में, अब आप क्वेरी में अभिव्यक्तियों का उपयोग कर सकते हैं, और उन मानों की गणना डेटाबेस में की जाएगी।

Foo.objects.order_by(F('a') - F('b')) 
Foo.objects.order_by(F('a') - F('bar__x')) 

यहां एक संपूर्ण runnable उदाहरण है कि इन भाव के साथ खेलता है है::

# Tested with Django 1.9.2 
import logging 
import sys 

import django 
from django.apps import apps 
from django.apps.config import AppConfig 
from django.conf import settings 
from django.db import connections, models, DEFAULT_DB_ALIAS 
from django.db.models import F 
from django.db.models.base import ModelBase 
from django.db.models.functions import Concat, Value 

from mock import patch, PropertyMock, MagicMock 

NAME = 'udjango' 


def main(): 

    setup() 

    class Bar(models.Model): 
     x = models.IntegerField() 

    class Foo(models.Model): 
     a = models.IntegerField() 
     b = models.IntegerField() 
     bar = models.ForeignKey(Bar) 

    syncdb(Bar) 
    syncdb(Foo) 

    bar1 = Bar.objects.create(x=1) 
    bar5 = Bar.objects.create(x=5) 
    Foo.objects.create(a=10, b=3, bar=bar1) 
    Foo.objects.create(a=13, b=3, bar=bar5) 
    Foo.objects.create(a=15, b=9, bar=bar1) 

    print(Foo.objects.annotate(ordering=F('a') - F('b')) 
      .order_by('ordering').values_list('a', 'b', 'bar__x', 'ordering')) 
    # >>> [(15, 9, 1, 6), (10, 3, 1, 7), (13, 3, 5, 10)] 

    print(Foo.objects.annotate(ordering=F('a') - F('bar__x')) 
      .order_by('ordering').values_list('a', 'b', 'bar__x', 'ordering')) 
    # >>> [(13, 3, 5, 8), (10, 3, 1, 9), (15, 9, 1, 14)] 

    print(Foo.objects.order_by(F('a') - F('b')).values_list('a', 'b', 'bar__x')) 
    # >>> [(15, 9, 1), (10, 3, 1), (13, 3, 5)] 

    print(Foo.objects.order_by(F('a') - F('bar__x')).values_list('a', 'b', 'bar__x')) 
    # >>> [(13, 3, 5), (10, 3, 1), (15, 9, 1)] 

    logging.info('Done.') 


def setup(): 
    db_file = NAME + '.db' 
    with open(db_file, 'w'): 
     pass # wipe the database 
    settings.configure(
     DEBUG=True, 
     DATABASES={ 
      DEFAULT_DB_ALIAS: { 
       'ENGINE': 'django.db.backends.sqlite3', 
       'NAME': db_file}}, 
     LOGGING={'version': 1, 
       'disable_existing_loggers': False, 
       'formatters': { 
        'debug': { 
         'format': '%(asctime)s[%(levelname)s]' 
            '%(name)s.%(funcName)s(): %(message)s', 
         'datefmt': '%Y-%m-%d %H:%M:%S'}}, 
       'handlers': { 
        'console': { 
         'level': 'DEBUG', 
         'class': 'logging.StreamHandler', 
         'formatter': 'debug'}}, 
       'root': { 
        'handlers': ['console'], 
        'level': 'INFO'}, 
       'loggers': { 
        "django.db": {"level": "DEBUG"}}}) 
    app_config = AppConfig(NAME, sys.modules['__main__']) 
    apps.populate([app_config]) 
    django.setup() 
    original_new_func = ModelBase.__new__ 

    # noinspection PyDecorator 
    @staticmethod 
    def patched_new(cls, name, bases, attrs): 
     if 'Meta' not in attrs: 
      class Meta: 
       app_label = NAME 
      attrs['Meta'] = Meta 
     return original_new_func(cls, name, bases, attrs) 
    ModelBase.__new__ = patched_new 


def syncdb(model): 
    """ Standard syncdb expects models to be in reliable locations. 

    Based on https://github.com/django/django/blob/1.9.3 
    /django/core/management/commands/migrate.py#L285 
    """ 
    connection = connections[DEFAULT_DB_ALIAS] 
    with connection.schema_editor() as editor: 
     editor.create_model(model) 

main() 
संबंधित मुद्दे