+1

Query Expressions in Django (Part II)

Query expression mô tả một giá trị hoặc một tính toán được sử dụng trong update, create, filter, order by, annotation hay aggregate. Có một số built-in expression có thể giúp bạn trong việc viết các câu query. Các expression có thể được kết hợp hoặc trong một số trường hợp là lồng nhau để thực hiện các tính toán phức tạp.

Supported arithmetic

Django hỗ trợ cộng, trừ, nhân, chia, số học modulo và lũy thừa ở các biểu thức query, cũng như là sử dụng hằng số, biến và thậm chí cả các expression khác trong Python.


Some examples

from django.db.models import F, Count
from django.db.models.functions import Length, Upper, Value

# Tìm các công ty có số lượng nhân viên nhiều hơn số lượng ghế ngồi
Company.objects.filter(num_employees__gt=F('num_chairs'))

# Tìm các công ty có số lượng nhân viên bằng ít nhất hai lần số lượng ghế ngồi
# Hai câu lệnh sau là tương đương
Company.objects.filter(num_employees__gt=F('num_chairs') * 2)
Company.objects.filter(num_employees__gt=F('num_chairs') + F('num_chairs'))

# Số lượng ghế cần mua thêm để tất cả nhân viên của mỗi công ty đều có ghế ngồi
company = Company.objects.filter(
    num_employees__gt=F('num_chairs')).annotate(
    chairs_needed=F('num_employees') - F('num_chairs')).first()
company.num_employees
=> 120

company.num_chairs
=> 50

company.chairs_needed
=> 70

# Thêm mới một công ty
company = Company.objects.create(name='Google', ticker=Upper(Value('goog')))
# Refresh lại object nếu muốn truy cập trường dữ liệu
company.refresh_from_db()
company.ticker
=> 'GOOG'

# Annotate model với một giá trị được tính toán
# Hai lệnh sau tương đương
Company.objects.annotate(num_products=Count('products'))
Company.objects.annotate(num_products=Count(F('products')))

# Aggregate cũng có thể gồm các tính toán phức tạp
Company.objects.annotate(num_offerings=Count(F('products') + F('services')))

# Các expression cũng có thể được sử dụng trong mệnh đề order_by
Company.objects.order_by(Length('name').asc())
Company.objects.order_by(Length('name').desc())

Built-in Expressions

Func() expressions

Expression Func() thuộc kiểu expression cơ bản liên quan đến các hàm cơ sở dữ liệu (database function) như là COALESCELOWER hay các aggregate như SUM. Chúng có thể được sử dụng một cách trực tiếp:

from django.db.models import Func, F

queryset.annotate(field_lower=Func(F('field'), function='LOWER'))

Hoặc được sử dụng để xây dựng một library gồm các hàm cơ sở dữ liệu:

class Lower(Func):
    function = 'LOWER'

queryset.annotate(field_lower=Lower('field'))

Cả hai trường hợp đều trả về một queryset mà ở đó, mỗi model được annotate với một attribute phụ field_lower từ câu SQL sau:

SELECT
    ...
    LOWER("db_table"."field") as "field_lower"

Click vào đây để xem danh sách hàm cơ sở dữ liệu có sẵn.

class Func(*expressions, **extra)[source]

  • function

    Một class attribute mô tả hàm sẽ được sinh ra. Đặc biệt, function sẽ được nội suy như placeholder function trong template ở bên dưới. Mặc định là None.

  • template

    Một class attribute, như là một format string, mô tả câu SQL được sinh ra cho hàm. Mặc định là "%(function)s(%(expressions)s)".

    Nếu bạn muốn viết một câu SQL kiểu như strftime("%W", "data") và có sử dụng ký tự % thông thường trong câu truy vấn, gấp bốn lần nó (%%%%) trong attribute template bởi vì string đó sẽ được nội suy hai lần: một là trong quá trình nội suy template ở as_sql() và một là trong quá trình nội suy SQL với các tham số truy vấn (query parameter) trong con trỏ cơ sở dữ liệu (database cursor).

  • arg_joiner

    Một class attribute chỉ rõ ký tự được sử dụng để nối list các expressions lại với nhau. Mặc định là ", ".

  • arity

    Mới trong Django 1.10
    

    Một class attribute chỉ ra số lượng tham biến mà hàm chấp nhận. Nếu attribute này được set và hàm được gọi với số lượng expression khác so với giá trị được set, lỗi TypeError sẽ được raise lên. Mặc định là None.

  • as_sql(compiler, connection, function=None, template=None, arg_joiner=None, **extra_context)[source]

    Sinh ra câu lệnh SQL cho hàm cơ sở dữ liệu (database function).

    Các method as_vendor() sẽ sử dụng function, template, arg_joiner và các parameter **extra_context khác để customise câu SQL khi cần. Ví dụ:

    # django/db/models/functions.py
    
    class ConcatPair(Func):
    ...
    function = 'CONCAT'
    ...
    
    def as_mysql(self, compiler, connection):
        return super(ConcatPair, self).as_sql(
            compiler, connection,
            function='CONCAT_WS',
            template="%(function)s('', %(expressions)s)",
        )
    
    Thay đổi trong Django 1.10:
    Hỗ trợ các parameter: arg_joiner, **extra_context
    

Tham biến *expressions là một list các expression vị trí mà hàm sẽ được apply. Các expression sẽ được convert thành các string, được nối với nhau với arg_joiner và sau đó được nội vào trong template như placeholder expressions.

Các tham biến vị trí có thể là các expression hoặc giá trị Python (Python value). Các string được giả định là các tham chiếu cột (column reference) và sẽ được wrap trong các expression F() trong khi các giá trị khác sẽ được wrap trong các expression Value().

*extra là các cặp key=value sẽ được nội suy vào trong attribute template. Các keyword function, templatearg_joiner có thể được sử dụng để thay thế các attribute cùng tên mà không cần phải định nghĩa class riêng. output_field có thể được sử dụng để định nghĩa kiểu trả về mong muốn.


Aggregate() expressions

Expression aggregate là một trường hợp đặc biệt của expression Func() cấu thành nên câu query mà ở đó sự xuất hiện của mệnh đề GROUP BY là bắt buộc. Tất cả các hàm aggregate, như kiểu Sum()Count(), kế thừa từ Aggregate().

Bởi vì Aggregate là một expression hoặc wrap các expression, bạn có thể biểu diễn các tính toán phức tạp:

from django.db.models import Count

Company.objects.annotate(
    managers_required=(Count('num_employees') / 4) + Count('num_managers'))

class Aggregate(expression, output_field=None, **extra)[source]

  • template

    Một class attribute, như là một format string, mô tả câu SQL được sinh ra cho aggregate. Mặc định là "%(function)s(%(expressions)s)".

  • function

    Một class attribute mô tả hàm aggregate sẽ được sinh ra. Đặc biệt, function sẽ được nội suy như placeholder function trong template. Mặc định là None.

Tham biến expression có thể là tên của một field trong model, hoặc một expression khác. Nó sẽ được convert thành một string và được sử dụng như placeholder expressions trong template.

Tham biến output_field yêu cầu một instance model field, kiểu như IntegerField() hoặc BooleanField(), mà ở đó Django sẽ load giá trị vào sau khi nó được lấy ra khỏi database. Thông thường không có tham biến nào là bắt buộc khi khởi tạo model field bởi vì bất cứ tham biến nào liên quan đến validate dữ liệu (max_length, max_digits, etc.) cũng sẽ không bị ép buộc ở giá trị đầu ra của expression (the expression's output value).

Chú ý rằng output_field chỉ cần thiết khi Django không thể xác định kiểu trường của kết quả. Các expression phức tạp trộn lẫn các các kiểu trường phải nên chỉ rõ output_field mong muốn. Ví dụ, cộng một IntegerField() với một FloatField() nên định nghĩa output_field=FloatField().

*extra là các cặp key=value sẽ được nội suy vào trong attribute template.


Creating your own Aggregate Functions

Tạo aggregate cho riêng bạn thực sự dễ dàng. Tối thiểu thì bạn phải định nghĩa function. Bạn cũng hoàn toàn có thể customise câu SQL được sinh ra. Ví dụ:

from django.db.models import Aggregate

class Count(Aggregate):
    function = 'COUNT'
    template = '%(function)s(%(distinct)s%(expressions)s)'

    def __init__(self, expression, distinct=False, **extra):
        super(Count, self).__init__(
            expression,
            distinct='DISTINCT ' if distinct else '',
            output_field=IntegerField(),
            **extra
        )

Value() expressions

class Value(value, output_field=None)[source]

Một object Value() biểu diễn component nhỏ nhất của một expression: một giá trị đơn giản. Khi bạn cần biểu diễn một giá trị integer, boolean hoặc string trong một expression, bạn có thể wrap giá trị đó trong một Value().

Bạn sẽ hiến khi sử dụng Value() một cách trực tiếp. Khi bạn viết expression F("field") + 1. Django sẽ ngầm wrap 1 trong một Value(). Điều này cho phép các giá trị đơn giản được sử dụng trong các expression phức tạp hơn. Bạn sẽ cần sử dụng Value() khi bạn muốn truyền một string vào một expression. Hầu hết các expression hiểu một tham biến string như là tên của một field, kiểu Lower("name").

Tham biến value mô tả giá trị trong expression, như là 1, True hay None. Django biết cách convert nhưng giá trị Python này thành các kiểu dữ liệu phù hợp.

Tham biến output_field nên là một instance model field, kiểu như IntegerField() hoặc BooleanField(), mà ở đó Django sẽ load giá trị vào sau khi nó được lấy ra khỏi database. Thông thường không có tham biến nào là bắt buộc khi khởi tạo model field bởi vì bất cứ tham biến nào liên quan đến validate dữ liệu cũng sẽ không bị ép buộc ở giá trị đầu ra của expression.


Phần II của loạt bài viết về Query Expressions in Django xin được dừng lại ở đây 😃

Source: https://docs.djangoproject.com/en/1.10/ref/models/expressions/


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí