【问题标题】:In Django models, how does the foreign key field know which field to match with in the other model?在 Django 模型中,外键字段如何知道与另一个模型中的哪个字段匹配?
【发布时间】:2021-12-19 13:33:43
【问题描述】:

我了解外键的作用,但我无法理解为什么它在 Django 中有效。

我在“app1/models.py”文件中有项目模型。这个模型有一个名为“owner”的 ForeignKey,它链接到我的“app2/models.py”文件中的 Profile 模型。

如果我只是将模型名称传递给“所有者”字段,项目模型中的“所有者”字段如何知道它应该链接到配置文件模型中的“用户”字段? 我觉得我应该在 Project 模型中传递 Profile.field 或类似的东西:

owner = models.ForeignKey(Profile.user, null=True, blank=True ... )

Dennis Ivy 教程中的完整模型代码:

class Project(models.Model):
    owner = models.ForeignKey(Profile, null=True, blank=True, on_delete=models.SET_NULL)
    title = models.CharField(max_length=200) #null is default=False and is required
    description = models.TextField(null=True, blank=True) #for a bigger field, allows null(for db)/blank(for Django get/post)
    featured_image = models.ImageField(null=True, blank=True, default='default.jpg')
    demo_link = models.CharField(max_length=2000)
    source_link = models.CharField(max_length=2000, null=True, blank=True) #can be blank in db
    tags = models.ManyToManyField('Tag', blank=True) #quotes will ref other class after this one 
    vote_total = models.IntegerField(default=0, null=True, blank=True)
    vote_ratio = models.IntegerField(default=0, null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True) #generate when model instance is created
    id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False) 

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True)
    name = models.CharField(max_length=200, blank=True, null=True)
    email = models.EmailField(max_length=500, blank=True, null=True)
    username = models.CharField(max_length=200, blank=True, null=True)
    location = models.CharField(max_length=200, blank=True, null=True)
    short_intro = models.CharField(max_length=200, blank=True, null=True)
    bio = models.TextField(blank=True, null=True)
    profile_image = models.ImageField(
        null=True, blank=True, upload_to='profiles/', default='profiles/user-default.png')
    social_github = models.CharField(max_length=200, blank=True, null=True)
    social_twitter = models.CharField(max_length=200, blank=True, null=True)
    social_linkedin = models.CharField(max_length=200, blank=True, null=True)
    social_youtube = models.CharField(max_length=200, blank=True, null=True)
    social_website = models.CharField(max_length=200, blank=True, null=True)
    created = models.DateTimeField(auto_now_add=True)
    id = models.UUIDField(default=uuid.uuid4, unique=True, 
                        primary_key=True, editable=False) 

【问题讨论】:

    标签: python python-3.x django django-models django-forms


    【解决方案1】:

    谢谢,维克。我现在已经解决了我的困惑。 ForeignKey 指定了一个模型,但它总是连接到该模型的主键,这就是我不必指定 Project.title 或 Project.id 的原因。然后,当输出带有 ForeignKey 的 Review 模型时,它会调用 Project 模型的 str 方法,该方法列出了标题。我没有意识到 str 方法被调用了。

    我没有在我的代码示例中包含 str 方法,因为我认为它与我的问题没有任何关系,但我现在明白 str 方法是我看到 Project.title 而不是 Project 的原因。身份证。

    def str(self): return self.title

    这是我缺少的拼图。 https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.ForeignKey.to_field

    ForeignKey.to_field
    关系所针对的相关对象上的字段。默认情况下,Django 使用相关对象的主键。如果您引用不同的字段,则该字段必须具有 unique=True。

    【讨论】:

      【解决方案2】:

      外键字段与另一个模型中的另一个字段不匹配。它与模型本身匹配。假设您想将名为“victor”的配置文件分配给新项目模型对象,它会像这样:

      from app_name.model import Project, Profile
      
      profile1=Profile.objects.filter(name='victor').first()
      new_project=Project(title='project 1',demolink='demo link',owner=profile1)
      

      在这里,我们将用户对象分配给项目的所有者属性,而不是字段。

      Profile.User 字段不是必需的。 如果你想创建一个customUser,我建议你查一下,否则你会给自己很多身份验证问题。您必须为其创建模型管理器,然后才能开始自定义。它应该是这样的:

      from django.db import models
      from django.contrib.auth.models import User, AbstractUser
      from django.utils.translation import ugettext_lazy as _
      from django.contrib.auth.base_user import BaseUserManager
      
      
      class CustomUserManager(BaseUserManager):
          """
          Custom user model manager where email is the unique identifiers
          for authentication instead of usernames.
          """
          def create_user(self, username, password, **extra_fields):
              """
              Create and save a User with the given username and password.
              """
              if not username:
                  raise ValueError(_('The Username must be set'))
              user = self.model(username=username, **extra_fields)
              user.set_password(password)
              extra_fields.setdefault('is_staff', True)
              extra_fields.setdefault('is_superuser', False)
              extra_fields.setdefault('is_active', True)
              user.save()
              return user
      
          def create_superuser(self, username, password, **extra_fields):
              """
              Create and save a SuperUser with the given username and password.
              """
              extra_fields.setdefault('is_staff', True)
              extra_fields.setdefault('is_superuser', True)
              extra_fields.setdefault('is_active', True)
      
              if extra_fields.get('is_staff') is not True:
                  raise ValueError(_('Superuser must have is_staff=True.'))
              if extra_fields.get('is_superuser') is not True:
                  raise ValueError(_('Superuser must have is_superuser=True.'))
              return self.create_user(username, password, **extra_fields)
      
      class Profile(AbstractUser):
      
          objects = CustomUserManager()
          name = models.CharField(max_length=200, blank=True, null=True)
          email = models.EmailField(max_length=500, blank=True, null=True)
          location = models.CharField(max_length=200, blank=True, null=True)
          short_intro = models.CharField(max_length=200, blank=True, null=True)
          bio = models.TextField(blank=True, null=True)
          profile_image = models.ImageField(
              null=True, blank=True, upload_to='profiles/', default='profiles/user-default.png')
          social_github = models.CharField(max_length=200, blank=True, null=True)
          social_twitter = models.CharField(max_length=200, blank=True, null=True)
          social_linkedin = models.CharField(max_length=200, blank=True, null=True)
          social_youtube = models.CharField(max_length=200, blank=True, null=True)
          social_website = models.CharField(max_length=200, blank=True, null=True)
          created = models.DateTimeField(auto_now_add=True)
          id = models.UUIDField(default=uuid.uuid4, unique=True, 
                              primary_key=True, editable=False) 
      

      【讨论】:

      • 我还是一头雾水。 Project.owner 字段如何知道与 Profile.user 而不是 Profile.name 匹配?
      • 我已经编辑了我的答案。我希望它足够清楚
      • 感谢您的回复,如果它对其他人有帮助,我会投票赞成。在我对 django 模型的理解中,我必须仍然缺少一些乐高积木,因为我仍然不明白它是如何工作的。在 SQL 中,您必须在分配外键时指定表和字段。但是 django 只是分配了其他模型...我不明白,因为如果第二个表不知道要匹配哪个字段,它如何使用 on_delete=models.CASCADE 删除原始表条目。
      • on_delete=models.CASCADE 仅表示在删除另一个模型时删除与另一个模型相关的模型。因为在某些字段中,您可能没有指定null=True,因此该字段不能被取消。 Django 将在数据库中留下一个无效的空格,因为它不能被取消。当您删除另一个模型时,它变为空,因为它找不到另一个模型,但它不能真正将值更改为空,因为您不允许这样做。如果要使用on_delete=models.SET_NULL,还必须为该字段指定null=True
      猜你喜欢
      • 1970-01-01
      • 2012-05-20
      • 2010-10-18
      • 1970-01-01
      • 1970-01-01
      • 2019-03-11
      • 2021-09-16
      • 2023-02-24
      • 1970-01-01
      相关资源
      最近更新 更多