ดูเหมือนว่าคุณกำลังถามถึงความแตกต่างระหว่างตัวแบบข้อมูลและตัวแบบโดเมน - แบบหลังคือที่ที่คุณสามารถค้นหาตรรกะทางธุรกิจและเอนทิตีที่รับรู้โดยผู้ใช้ปลายทางของคุณอดีตคือที่ที่คุณเก็บข้อมูลของคุณ
นอกจากนี้ฉันได้ตีความส่วนที่ 3 ของคำถามของคุณเป็น: วิธีสังเกตความล้มเหลวในการแยกแบบจำลองเหล่านี้ออก
นี่เป็นแนวคิดที่แตกต่างกันสองอย่างและมันยากที่จะแยกพวกเขาออกจากกันเสมอ อย่างไรก็ตามมีรูปแบบและเครื่องมือทั่วไปบางอย่างที่สามารถใช้เพื่อจุดประสงค์นี้ได้
เกี่ยวกับรูปแบบโดเมน
สิ่งแรกที่คุณต้องจดจำคือโมเดลโดเมนของคุณไม่ได้เกี่ยวกับข้อมูลจริงๆ มันเกี่ยวกับการกระทำและคำถามเช่น "เปิดใช้งานผู้ใช้รายนี้", "ปิดใช้งานผู้ใช้รายนี้", "ผู้ใช้รายใดที่เปิดใช้งานอยู่ในขณะนี้" และ "ชื่อผู้ใช้รายนี้คืออะไร" ในแง่คลาสสิก: มันเป็นเรื่องของคำสั่งและคำสั่ง
คิดในคำสั่ง
เริ่มต้นด้วยการดูคำสั่งในตัวอย่างของคุณ: "เปิดใช้งานผู้ใช้รายนี้" และ "ปิดการใช้งานผู้ใช้รายนี้" สิ่งที่ดีเกี่ยวกับคำสั่งก็คือพวกเขาสามารถแสดงได้อย่างง่ายดายด้วยสถานการณ์สมมติขนาดเล็กเมื่อ - ในขณะนั้น:
ให้ผู้ใช้ที่ไม่ได้ใช้งาน
เมื่อผู้ดูแลระบบเปิดใช้งานผู้ใช้นี้
จากนั้นผู้ใช้จะเปิดใช้งาน
และอีเมลยืนยันถูกส่งไปยังผู้ใช้
และรายการจะถูกเพิ่มลงในบันทึกของระบบ
(ฯลฯ ฯลฯ )
สถานการณ์ดังกล่าวมีประโยชน์ในการดูว่าส่วนต่าง ๆ ของโครงสร้างพื้นฐานของคุณสามารถได้รับผลกระทบจากคำสั่งเดียวได้อย่างไรในกรณีนี้ฐานข้อมูลของคุณ (การตั้งค่าสถานะ 'active' บางชนิด), เมลเซิร์ฟเวอร์ของคุณ, บันทึกระบบของคุณ ฯลฯ
สถานการณ์ดังกล่าวยังช่วยคุณในการตั้งค่าสภาพแวดล้อมการพัฒนาที่ขับเคลื่อนด้วยการทดสอบ
และสุดท้ายการคิดคำสั่งจะช่วยให้คุณสร้างแอปพลิเคชันที่เน้นงาน ผู้ใช้ของคุณจะประทับใจในสิ่งนี้ :-)
คำสั่งการแสดง
Django มีสองวิธีง่ายๆในการแสดงคำสั่ง; เป็นทั้งตัวเลือกที่ถูกต้องและไม่ผิดปกติที่จะผสมสองวิธีเข้าด้วยกัน
ชั้นบริการ
โมดูลบริการได้รับการอธิบายโดย @Hedde ที่นี่คุณกำหนดโมดูลแยกและแต่ละคำสั่งจะแสดงเป็นฟังก์ชั่น
services.py
def activate_user(user_id):
user = User.objects.get(pk=user_id)
# set active flag
user.active = True
user.save()
# mail user
send_mail(...)
# etc etc
ใช้แบบฟอร์ม
อีกวิธีคือใช้แบบฟอร์ม Django สำหรับแต่ละคำสั่ง ฉันชอบวิธีนี้มากกว่าเพราะมันรวมแง่มุมต่าง ๆ ที่เกี่ยวข้องอย่างใกล้ชิด:
- การดำเนินการของคำสั่ง (มันทำอะไร?)
- การตรวจสอบความถูกต้องของพารามิเตอร์คำสั่ง (สามารถทำได้หรือไม่)
- การนำเสนอคำสั่ง (ฉันจะทำสิ่งนี้ได้อย่างไร)
forms.py
class ActivateUserForm(forms.Form):
user_id = IntegerField(widget = UsernameSelectWidget, verbose_name="Select a user to activate")
# the username select widget is not a standard Django widget, I just made it up
def clean_user_id(self):
user_id = self.cleaned_data['user_id']
if User.objects.get(pk=user_id).active:
raise ValidationError("This user cannot be activated")
# you can also check authorizations etc.
return user_id
def execute(self):
"""
This is not a standard method in the forms API; it is intended to replace the
'extract-data-from-form-in-view-and-do-stuff' pattern by a more testable pattern.
"""
user_id = self.cleaned_data['user_id']
user = User.objects.get(pk=user_id)
# set active flag
user.active = True
user.save()
# mail user
send_mail(...)
# etc etc
กำลังคิดในการค้นหา
ตัวอย่างเช่นคุณไม่มีข้อความค้นหาใด ๆ ดังนั้นฉันจึงใช้เสรีภาพในการสร้างข้อความค้นหาที่มีประโยชน์สองสามข้อ ฉันชอบที่จะใช้คำว่า "คำถาม" แต่คำค้นหาคือคำศัพท์ดั้งเดิม ข้อความค้นหาที่น่าสนใจคือ: "ชื่อของผู้ใช้นี้คืออะไร", "ผู้ใช้นี้สามารถเข้าสู่ระบบได้หรือไม่", "แสดงรายการผู้ใช้ที่ไม่ได้ใช้งาน" ให้ฉันดูและ "การกระจายทางภูมิศาสตร์ของผู้ใช้ที่ปิดใช้งานคืออะไร"
ก่อนที่จะเริ่มตอบคำถามเหล่านี้คุณควรถามตัวเองด้วยคำถามสองข้อเสมอ: นี่คือแบบสอบถามแบบนำเสนอเฉพาะสำหรับแม่แบบของฉันและ / หรือแบบสอบถามตรรกะทางธุรกิจที่เชื่อมโยงกับการดำเนินการคำสั่งของฉันและ / หรือแบบสอบถามแบบรายงาน
คำสั่งที่นำเสนอเป็นเพียงการปรับปรุงส่วนติดต่อผู้ใช้ คำตอบของการค้นหาตรรกะทางธุรกิจส่งผลโดยตรงต่อการดำเนินการตามคำสั่งของคุณ การรายงานการสืบค้นนั้นมีวัตถุประสงค์เพื่อการวิเคราะห์และมีข้อ จำกัด ด้านเวลาเท่านั้น หมวดหมู่เหล่านี้ไม่ได้เกิดขึ้นพร้อมกัน
อีกคำถามคือ: "ฉันสามารถควบคุมคำตอบได้ทั้งหมดหรือไม่" ตัวอย่างเช่นเมื่อสอบถามชื่อผู้ใช้ (ในบริบทนี้) เราไม่สามารถควบคุมผลลัพธ์ได้เนื่องจากเราใช้ API ภายนอก
การทำแบบสอบถาม
แบบสอบถามพื้นฐานที่สุดใน Django คือการใช้วัตถุ Manager:
User.objects.filter(active=True)
แน่นอนว่าจะใช้งานได้ก็ต่อเมื่อข้อมูลนั้นแสดงจริงในตัวแบบข้อมูลของคุณ นี่ไม่ใช่กรณีเสมอไป ในกรณีเหล่านี้คุณสามารถพิจารณาตัวเลือกด้านล่าง
แท็กและตัวกรองที่กำหนดเอง
ทางเลือกแรกมีประโยชน์สำหรับข้อความค้นหาที่เป็นเพียงการนำเสนอ: แท็กที่กำหนดเองและตัวกรองแม่แบบ
template.html
<h1>Welcome, {{ user|friendly_name }}</h1>
template_tags.py
@register.filter
def friendly_name(user):
return remote_api.get_cached_name(user.id)
วิธีการค้นหา
หากการสืบค้นของคุณไม่เพียงแค่การนำเสนอคุณสามารถเพิ่มการสืบค้นไปยังservices.pyของคุณ(หากคุณกำลังใช้งานอยู่) หรือแนะนำโมดูลquery.py :
queries.py
def inactive_users():
return User.objects.filter(active=False)
def users_called_publysher():
for user in User.objects.all():
if remote_api.get_cached_name(user.id) == "publysher":
yield user
รุ่นพร็อกซี
รุ่นพร็อกซีมีประโยชน์อย่างมากในบริบทของตรรกะทางธุรกิจและการรายงาน คุณกำหนดชุดย่อยที่ปรับปรุงแล้วของโมเดล คุณสามารถแทนที่ QuerySet พื้นฐานของผู้จัดการได้โดยการแทนที่Manager.get_queryset()
เมธอด
models.py
class InactiveUserManager(models.Manager):
def get_queryset(self):
query_set = super(InactiveUserManager, self).get_queryset()
return query_set.filter(active=False)
class InactiveUser(User):
"""
>>> for user in InactiveUser.objects.all():
… assert user.active is False
"""
objects = InactiveUserManager()
class Meta:
proxy = True
แบบจำลองการสืบค้น
สำหรับแบบสอบถามที่มีความซับซ้อนโดยเนื้อแท้ แต่มีการดำเนินการค่อนข้างบ่อยมีความเป็นไปได้ของรูปแบบแบบสอบถาม รูปแบบแบบสอบถามเป็นรูปแบบของการทำให้เป็นปกติซึ่งข้อมูลที่เกี่ยวข้องสำหรับแบบสอบถามเดียวจะถูกเก็บไว้ในรูปแบบที่แยกต่างหาก เคล็ดลับของหลักสูตรคือการทำให้รูปแบบที่ไม่สอดคล้องกันให้สอดคล้องกับรูปแบบหลัก แบบจำลองการสืบค้นสามารถใช้ได้เมื่อการเปลี่ยนแปลงทั้งหมดอยู่ภายใต้การควบคุมของคุณ
models.py
class InactiveUserDistribution(models.Model):
country = CharField(max_length=200)
inactive_user_count = IntegerField(default=0)
ตัวเลือกแรกคือการอัพเดตโมเดลเหล่านี้ในคำสั่งของคุณ สิ่งนี้มีประโยชน์มากหากโมเดลเหล่านี้ถูกเปลี่ยนโดยคำสั่งหนึ่งหรือสองคำสั่งเท่านั้น
forms.py
class ActivateUserForm(forms.Form):
# see above
def execute(self):
# see above
query_model = InactiveUserDistribution.objects.get_or_create(country=user.country)
query_model.inactive_user_count -= 1
query_model.save()
ตัวเลือกที่ดีกว่าคือการใช้สัญญาณที่กำหนดเอง สัญญาณเหล่านี้แน่นอนที่ปล่อยออกมาโดยคำสั่งของคุณ สัญญาณมีข้อได้เปรียบที่คุณสามารถเก็บแบบจำลองแบบสอบถามหลายแบบให้ตรงกับรุ่นเดิมของคุณ นอกจากนี้การประมวลผลสัญญาณสามารถ offloaded เพื่องานพื้นหลังโดยใช้ Celery หรือกรอบที่คล้ายกัน
signals.py
user_activated = Signal(providing_args = ['user'])
user_deactivated = Signal(providing_args = ['user'])
forms.py
class ActivateUserForm(forms.Form):
# see above
def execute(self):
# see above
user_activated.send_robust(sender=self, user=user)
models.py
class InactiveUserDistribution(models.Model):
# see above
@receiver(user_activated)
def on_user_activated(sender, **kwargs):
user = kwargs['user']
query_model = InactiveUserDistribution.objects.get_or_create(country=user.country)
query_model.inactive_user_count -= 1
query_model.save()
รักษาความสะอาด
เมื่อใช้วิธีการนี้จะทำให้ทราบได้ง่ายว่ารหัสของคุณยังสะอาดอยู่หรือไม่ เพียงทำตามแนวทางเหล่านี้:
- แบบจำลองของฉันมีวิธีการมากกว่าจัดการสถานะฐานข้อมูลหรือไม่ คุณควรแยกคำสั่ง
- โมเดลของฉันมีคุณสมบัติที่ไม่ได้แม็พกับฟิลด์ฐานข้อมูลหรือไม่? คุณควรแยกแบบสอบถาม
- โมเดลอ้างอิงโครงสร้างพื้นฐานที่ไม่ใช่ฐานข้อมูลของฉัน (เช่นเมล) หรือไม่ คุณควรแยกคำสั่ง
การเข้าชมแบบเดียวกัน (เพราะมุมมองมักประสบปัญหาเดียวกัน)
- มุมมองของฉันจัดการโมเดลฐานข้อมูลอย่างแข็งขันหรือไม่? คุณควรแยกคำสั่ง
อ้างอิงบางส่วน
เอกสาร Django: รุ่นพร็อกซี
เอกสาร Django: สัญญาณ
สถาปัตยกรรม: การออกแบบโดเมนขับเคลื่อน