В моделях Django, как поле внешнего ключа узнает, с каким полем сопоставить поле в другой модели?
Я понимаю, что делают внешние ключи, но мне трудно понять, почему это работает в Django.
У меня есть модель Project в файле 'app1/models.py'. Эта модель имеет ForeignKey с именем 'owner', который ссылается на модель Profile в моем файле 'app2/models.py'
Как поле 'owner' в модели Project узнает, что оно должно быть связано с полем 'user' в модели Profile, если я передаю только имя модели полю 'owner'? Мне кажется, что я должен передавать Profile.field или что-то вроде этого в модели Project:
owner = models.ForeignKey(Profile.user, null=True, blank=True ... )
Полный код модели из учебника Денниса Айви:
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)
Поле с внешним ключом не совпадает с другим полем в другой модели. оно совпадает с самой моделью.Допустим, вы хотите присвоить профиль с именем '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)
здесь мы присвоили объект пользователя свойству owner проекта, а не полю.
Поле 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)
Спасибо, Вик. Теперь я разрешил свою путаницу. ForeignKey указывает модель, но он всегда подключается к первичному ключу этой модели, поэтому мне не нужно указывать Project.title или Project.id. Затем, когда модель Review с ForeignKey выводится, она вызывает метод str модели Project, который перечисляет название. Я не знал, что вызывается метод str.
Я не включил метод str в свой пример кода, потому что не думал, что он имеет отношение к моей проблеме, но теперь я понимаю, что метод str является причиной того, что я вижу Project.title вместо Project.id.
def str(self): return self.title