Django中两个不同模型字段关联问题咨询
Play and FGXP Models in Django? Absolutely—you absolutely should establish an explicit relationship between these two models. This isn’t just a "nice-to-have"—it’ll make your code cleaner, your queries more efficient, and your data more reliable. Let me break down why and how to do it right.
Why You Need the Relationship
- Leverage Django ORM’s Power: Instead of writing manual JOIN SQL queries, you can use Django’s built-in relationship methods like
select_related()orprefetch_related()to fetch associated data in a single (or optimized) query. This eliminates slow N+1 query patterns and makes your code far more readable. - Guarantee Data Integrity: Django will enforce database-level foreign key constraints, so you can’t have an
FGXPrecord pointing to apidthat doesn’t exist inPlay. You also get control over what happens when aPlayis deleted (e.g., cascade delete the associatedFGXP, or block deletion entirely). - Clearer Code for Your Team: An explicit relationship makes it immediately obvious to other developers (or future you) how these two models connect—no guessing about random
pidfields being manually linked.
How to Implement It
First, let’s assume your existing Play model looks something like this (adjust field types to match your database):
from django.db import models class Play(models.Model): pid = models.CharField(primary_key=True, max_length=64) # Or IntegerField, depending on your setup score = models.BooleanField(verbose_name="Is a scoring play?") # Add other Play fields (e.g., game_id, play_type, yardage) here
Option 1: One-to-One Relationship (Most Likely for Your Use Case)
Since a single scoring Play should only have one set of extra point data in FGXP, a OneToOneField is the perfect fit. It enforces uniqueness and makes querying straightforward:
class FGXP(models.Model): # Map the relationship to Play's pid, using db_column to match your existing FGXP table's field name play = models.OneToOneField( Play, on_delete=models.CASCADE, # Delete FGXP if Play is removed (adjust to PROTECT if needed) db_column='pid', primary_key=True ) # Add your extra point fields here (e.g., made, blocked, attempt_type) made = models.BooleanField(verbose_name="Extra point made?") attempt_type = models.CharField(max_length=32, choices=[('extra', 'Extra Point'), ('two_point', 'Two-Point Conversion')])
Option 2: Foreign Key (If One Play Could Have Multiple FGXP Entries)
If your business logic allows a single Play to have multiple extra point records (uncommon for football, but possible), use a ForeignKey instead:
class FGXP(models.Model): play = models.ForeignKey( Play, on_delete=models.CASCADE, db_column='pid', related_name='extra_points' # Lets you query play.extra_points.all() ) # Your FGXP fields here
Example Queries
With the relationship set up, querying becomes trivial:
# Get all scoring plays with their associated extra point data scoring_plays_with_extra = Play.objects.filter(score=True).select_related('fgxp') for play in scoring_plays_with_extra: print(f"Play {play.pid}: Extra point {'made' if play.fgxp.made else 'missed'}")
Quick Note for Existing Databases
If your tables already have data, make sure all pid values in FGXP match valid pids in Play before adding the relationship. If there are orphaned records, you’ll need to clean them up or set null=True on the relationship temporarily (then fix the data).
内容的提问来源于stack exchange,提问作者JohnB

