Bài đăng trên blog này trình bày cách sử dụng Trường mô hình dành riêng cho PostgreSQL mới được giới thiệu trong Django 1.8 - Trường ArrayField, HStoreField và Phạm vi.
Bài đăng này là dành riêng cho những người ủng hộ tuyệt vời của chiến dịch Kickstarter này do Marc Tamlyn, một playa thực sự đã làm nên điều đó lại với nhau.
Câu lạc bộ Playaz?
Vì tôi là một người rất ham chơi và không có cơ hội tham gia vào Câu lạc bộ Playaz thực sự (và bởi vì ngày 4 Tây là bom xịt), tôi đã quyết định xây dựng Câu lạc bộ Playaz trực tuyến ảo của riêng mình. Đó chính xác là gì? Một mạng xã hội riêng tư, chỉ dành cho những người được mời, nhắm mục tiêu đến một nhóm nhỏ những người có cùng chí hướng.
Đối với bài đăng này, chúng tôi sẽ tập trung vào mô hình người dùng và khám phá cách các tính năng PostgreSQL mới của Django hỗ trợ mô hình. Các tính năng mới mà chúng tôi đang đề cập đến chỉ dành cho PostgreSQL, vì vậy đừng bận tâm thử tính năng này trừ khi bạn có cơ sở dữ liệu của mình ENGINE
bằng django.db.backends.postgresql_psycopg2
. Bạn sẽ cần phiên bản> =2.5 của psycopg2
. Aight playa, hãy làm điều này.
Holla nếu bạn với tôi! :)
Tạo mẫu cho Đại diện của Playa
Mỗi playa đều có một đại diện và họ muốn cả thế giới biết về đại diện của họ. Vì vậy, hãy tạo một hồ sơ người dùng (hay còn gọi là “người đại diện”) cho phép mỗi người chơi của chúng ta thể hiện cá tính của họ.
Đây là mô hình cơ bản cho một đại diện playaz:
from django.db import models
from django.contrib.auth.models import User
class Rep(models.Model):
playa = models.OneToOneField(User)
hood = models.CharField(max_length=100)
area_code = models.IntegerField()
Không có gì cụ thể cho 1.8 ở trên. Chỉ là một mô hình tiêu chuẩn để mở rộng Người dùng Django cơ sở, vì một playa vẫn cần tên người dùng và địa chỉ email, phải không? Ngoài ra, chúng tôi đã thêm hai trường mới để lưu trữ mã vùng và mui xe playaz.
Bankroll và RangeField
Đối với một vở kịch, không phải lúc nào việc che đậy nắp đậy của bạn cũng là đủ. Playaz thường thích phô trương tài sản ngân hàng của họ, nhưng đồng thời không muốn cho mọi người biết chính xác tài sản ngân hàng đó lớn như thế nào. Chúng ta có thể lập mô hình đó bằng một trong các Trường Phạm vi Postgres mới. Tất nhiên, chúng tôi sẽ sử dụng BigIntegerRangeField
để mô hình hóa các chữ số lớn tốt hơn, bạn phải không?
bankroll = pgfields.BigIntegerRangeField(default=(10, 100))
Các trường phạm vi dựa trên các đối tượng Phạm vi psycopg2 và có thể được sử dụng cho Numeric và DateRanges. Với trường bankroll được di chuyển vào cơ sở dữ liệu, chúng tôi có thể tương tác với các trường phạm vi của mình bằng cách chuyển cho nó một đối tượng phạm vi, vì vậy việc tạo playa đầu tiên của chúng tôi sẽ trông giống như sau:
>>>>>> from playa.models import Rep
>>> from django.contrib.auth.models import User
>>> calvin = User.objects.create_user(username="snoop", password="dogg")
>>> calvins_rep = Rep(hood="Long Beach", area_code=213)
>>> calvins_rep.bankroll = (100000000, 150000000)
>>> calvins_rep.playa = calvin
>>> calvins_rep.save()
Lưu ý dòng này:calvins_rep.bankroll = (100000000, 150000000)
. Ở đây chúng tôi đang thiết lập một trường phạm vi bằng cách sử dụng một bộ tuple đơn giản. Cũng có thể đặt giá trị bằng NumericRange
đối tượng như vậy:
from psycopg2.extras import NumericRange
br = NumericRange(lower=100000000, upper=150000000)
calvin.rep.bankroll = br
calvin.rep.save()
Điều này về cơ bản giống như sử dụng tuple. Tuy nhiên, điều quan trọng là phải biết về NumericRange
đối tượng được sử dụng để lọc mô hình. Ví dụ:nếu chúng tôi muốn tìm tất cả các vở kịch có tài khoản ngân hàng lớn hơn 50 triệu (có nghĩa là toàn bộ phạm vi tài khoản ngân hàng lớn hơn 50 triệu):
Rep.objects.filter(bankroll__fully_gt=NumericRange(50000000, 50000000))
Và điều đó sẽ trả về danh sách các vở kịch đó. Ngoài ra, nếu chúng tôi muốn tìm tất cả các vở kịch có tài khoản ngân hàng "ở đâu đó trong khoảng 10 đến 15 triệu", chúng tôi có thể sử dụng:
Rep.objects.filter(bankroll__overlap=NumericRange(10000000, 15000000))
Điều này sẽ trả lại tất cả các vở kịch có phạm vi tài khoản ngân hàng bao gồm ít nhất một số phần của phạm vi 10 đến 15 triệu. Một truy vấn tuyệt đối hơn sẽ là tất cả các vở kịch có tổng tài khoản ngân hàng nằm trong khoảng, tức là mọi người đang kiếm được ít nhất 10 triệu nhưng không quá 15 triệu. Truy vấn đó sẽ giống như sau:
Rep.objects.filter(bankroll__contained_by=NumericRange(10000000, 15000000))
Có thể tìm thấy thêm thông tin về các truy vấn dựa trên Phạm vi tại đây.
Skillz dưới dạng ArrayField
Đó không phải là tất cả về bankroll, playaz có skillz, tất cả các loại skillz. Hãy lập mô hình những thứ đó bằng ArrayField.
skillz = pgfields.ArrayField(
models.CharField(max_length=100, blank=True),
blank = True,
null = True,
)
Để khai báo ArrayField
chúng ta phải cung cấp cho nó một đối số đầu tiên, đó là trường cơ sở. Không giống như danh sách Python, ArrayFields phải khai báo mỗi phần tử của danh sách là cùng một kiểu. Trường cơ sở khai báo đây là kiểu nào và nó có thể là bất kỳ kiểu trường mô hình chuẩn nào. Trong trường hợp trên, chúng tôi vừa sử dụng CharField
dưới dạng basetype của chúng tôi, có nghĩa là skillz
sẽ là một mảng các chuỗi.
Lưu trữ các giá trị vào ArrayField
chính xác như bạn mong đợi:
>>> from django.contrib.auth.models import User
>>> calvin = User.objects.get(username='snoop')
>>> calvin.rep.skillz = ['ballin', 'rappin', 'talk show host', 'merchandizn']
>>> calvin.rep.save()
Tìm vở kịch bằng skillz
Nếu chúng ta cần một vở kịch cụ thể với một kỹ năng cụ thể, chúng ta sẽ tìm chúng bằng cách nào? Sử dụng __contains
bộ lọc:
Rep.objects.filter(skillz__contains=['rappin'])
Đối với những người chơi có bất kỳ kỹ năng nào trong số các kỹ năng [‘rappin’, ‘djing’, ‘production’] nhưng không có kỹ năng nào khác, bạn có thể thực hiện một truy vấn như sau:
Rep.objects.filter(skillz__contained_by=['rappin', 'djing', 'producing'])
Hoặc nếu bạn muốn tìm bất kỳ ai có bất kỳ kỹ năng nào trong danh sách nhất định:
Rep.objects.filter(skillz__overlap=['rappin', 'djing', 'producing'])
Bạn thậm chí có thể chỉ tìm thấy những người liệt kê một kỹ năng là kỹ năng đầu tiên của họ (vì mọi người đều liệt kê kỹ năng tốt nhất của họ trước):
Rep.objects.filter(skillz__0='ballin')
Trò chơi với tư cách là HStore
Trò chơi có thể được coi như một danh sách các kỹ năng ngẫu nhiên, linh tinh mà một người chơi có thể có. Vì Trò chơi bao gồm tất cả mọi thứ, hãy lập mô hình nó như một trường HStore, về cơ bản có nghĩa là chúng ta có thể gắn bất kỳ từ điển Python cũ nào vào đó:
game = pgfields.HStoreField()
Hãy dành một giây để suy nghĩ về những gì chúng tôi vừa làm ở đây. HStore là khá lớn. Về cơ bản, nó cho phép lưu trữ dữ liệu kiểu “NoSQL”, ngay bên trong postgreSQL. Ngoài ra, vì nó nằm bên trong PostgreSQL nên chúng tôi có thể liên kết (thông qua các khóa ngoại), các bảng chứa dữ liệu NoSQL với các bảng lưu trữ dữ liệu kiểu SQL thông thường. Bạn thậm chí có thể lưu trữ cả hai trong cùng một bảng trên các cột khác nhau như chúng tôi đang làm ở đây. Có lẽ playas không cần sử dụng jankie, MongoDB toàn nói chuyện đó nữa…
Quay lại chi tiết triển khai, nếu bạn cố gắng di chuyển trường HStore mới vào cơ sở dữ liệu và gặp phải lỗi này-
django.db.utils.ProgrammingError: type "hstore" does not exist
-khi đó cơ sở dữ liệu PostgreSQL của bạn là trước 8.1 (đã đến lúc nâng cấp, playa) hoặc chưa cài đặt tiện ích mở rộng HStore. Hãy nhớ rằng trong PostgreSQL, phần mở rộng HStore được cài đặt trên mỗi cơ sở dữ liệu và không phải trên toàn hệ thống. Để cài đặt nó từ dấu nhắc psql của bạn, hãy chạy SQL sau:
CREATE EXTENSION hstore
Hoặc nếu muốn, bạn có thể làm điều đó thông qua SQL Migration với tệp di chuyển sau (giả sử bạn đã kết nối với cơ sở dữ liệu với tư cách là superuser):
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.RunSQL("CREATE EXTENSION IF NOT EXISTS hstore")
]
Cuối cùng, bạn cũng cần đảm bảo rằng bạn đã thêm 'django.contrib.postgres'
tới 'settings.INSTALLED_APPS'
để sử dụng các trường HStore.
Với thiết lập đó, chúng tôi có thể thêm dữ liệu vào HStoreField
của mình game
bằng cách ném từ điển vào nó như vậy:
>>> calvin = User.objects.get(username="snoop")
>>> calvin.rep.game = {'best_album': 'Doggy Style', 'youtube-channel': \
'https://www.youtube.com/user/westfesttv', 'twitter_follows' : '11000000'}
>>> calvin.rep.save()
Hãy nhớ rằng dict chỉ được sử dụng chuỗi cho tất cả các khóa và giá trị.
Và bây giờ là một số ví dụ thú vị hơn…
Propz
Hãy viết một hàm “trò chơi hiển thị” để tìm kiếm trò chơi playaz và trả về danh sách các trò chơi phù hợp. Nói cách khác, chúng tôi đang tìm kiếm thông qua trường HStore để tìm bất kỳ khóa nào được chuyển vào hàm. Nó trông giống như sau:
def show_game(key):
return Rep.Objects.filter(game__has_key=key).values('game','playa__username')
Ở trên, chúng tôi đã sử dụng has_key
lọc cho trường HStore để trả về một bộ truy vấn, sau đó lọc thêm nó bằng hàm giá trị (chủ yếu để cho thấy rằng bạn có thể chuỗi django.contrib.postgres
nội dung với bộ truy vấn thông thường).
Giá trị trả về sẽ là danh sách các từ điển:
[
{'playa__username': 'snoop',
'game': {'twitter_follows': '11000000',
'youtube-channel': 'https://www.youtube.com/user/westfesttv',
'best_album': 'Doggy Style'
}
}
]
Như họ nói, Trò chơi nhận ra Trò chơi và bây giờ chúng tôi cũng có thể tìm kiếm trò chơi.
Con lăn cao
Nếu chúng ta tin những gì người chơi đang nói với chúng ta về các tờ tiền của họ, thì chúng ta có thể sử dụng điều đó để xếp chúng vào các danh mục (vì đó là một phạm vi). Hãy thêm xếp hạng Playa dựa trên danh sách ngân hàng với các cấp độ sau:
-
đồng tiền trẻ - tài khoản ngân hàng ít hơn một trăm nghìn đô
-
balla - cuộn ngân hàng từ 100.000 đến 500.000 với kỹ năng ‘ballin’
-
playa - bankroll từ 500.000 đến 1.000.000 với hai skillz và một số trò chơi
-
con lăn cao - cuộn ngân hàng lớn hơn 1.000.000
-
O.G. - có kỹ năng ‘gangsta’ và phím trò chơi “old-school”
Truy vấn cho balla ở bên dưới. Đây sẽ là cách diễn giải chặt chẽ, sẽ chỉ trả về những người có toàn bộ phạm vi tài khoản ngân hàng nằm trong giới hạn được chỉ định:
Rep.objects.filter(bankroll__contained_by=[100000, 500000], skillz__contains=['ballin'])
Hãy tự thử phần còn lại để luyện tập. Nếu bạn cần trợ giúp, hãy đọc Tài liệu.