Как получить доступ к объектам (информация о пользователе) через модели Django? (Внешний ключ, Новичок)
В моем проекте Django есть модель Profile и модель Post:
class Profile(models.Model):
""" User profile data """
user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='profile')
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
id_user = models.IntegerField()
bio = models.TextField(max_length=280, blank=True)
def __str__(self):
return str(self.user)
class Post(models.Model):
""" Model for user-created posts """
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='post')
post_text = models.TextField()
created_at = models.DateTimeField(default=datetime.now)
def __str__(self):
return self.user
Я хочу иметь возможность отображать имя_и фамилию пользователя в его сообщениях (которые отображаются в index.html вместе с другими сообщениями всех пользователей), наряду с его именем пользователя.
{% for post in posts %}
<div class="row d-flex justify-content-between">
<h5 class="col-12 col-md-6">{{ first_name here }} {{ last_name here }}</h5>
<h5 class="col-12 col-md-6">(@{{ post.user }})</h5>
</div>
Я могу нормально отобразить имя пользователя (post.user
выше), потому что оно находится в модели Post. Однако имя_и фамилия находятся в модели Profile, и я просто не могу найти способ получить к ним доступ.
Вид:
@login_required(login_url='signin')
def index(request):
""" View to render index """
user_object = User.objects.get(username=request.user.username)
user_profile = Profile.objects.get(user=user_object)
posts = Post.objects.all()
context = {'user_profile': user_profile, 'posts': posts, }
return render(request, 'index.html', context)
Помимо получения всех объектов Post, мое представление содержит ссылку на текущего вошедшего пользователя (user_object
) и его профиль (user_profile
) для настройки вещей в навигационной панели и ссылок на профиль вошедшего пользователя.
Исходя из этого, по моей логике, я должен добавить в представление что-то вроде этого:
post_author = Post.user
author_profile = Profile.objects.get(user=post_author)
чтобы я мог ссылаться на что-то вроде этого в цикле {% for post in posts %}:
{{ post_author.profile.first_name }} {{ author.profile.last_name }}
Но, конечно, это не работает:
TypeError at /. Поле 'id' ожидало число, но получило объект <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor at 0x7f176924f160>. Метод запроса: GET URL запроса: http://localhost:8000/ Версия Django: 3.2.13 Тип исключения: TypeError Значение исключения:
Поле 'id' ожидало число, но получило <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor object at 0x7f176924f160>.
Я следовал учебнику, но он не охватывает это, и я бы очень хотел выяснить, где я ошибаюсь, и получить лучшее понимание вещей. Если в принципе я смогу сделать это правильно, я также смогу добавлять avatar
к каждому сообщению. С другой стороны, может быть, это просто невозможно сделать так, как я настроил свой проект?
Вы на правильном пути!
"Ключ" здесь - это то, как вещи следуют отношениям типа foreignkey (многие к одному).
В настоящее время вы находитесь в своем шаблоне, просматривая свои сообщения. У вас есть нужная информация, осталось только получить к ней доступ. В шаблоне вы можете следовать за внешним ключом просто с помощью точки. Итак, мы хотим перейти от post -> fk -> user -> reverse fk -> profile -> field
(об обратном fk мы будем беспокоиться позже, сейчас просто знайте, что мы можем использовать связанное имя для его обхода)
Так что это должно работать:
{% for post in posts %}
<div class="row d-flex justify-content-between">
<h5 class="col-12 col-md-6">{{ post.user.profile[0].first_name }} {{ post.user.profile[0].first_name }}</h5>
<h5 class="col-12 col-md-6">(@{{ post.user }})</h5>
</div>
Хотя это не идеально...
Мы использовали profile[0], потому что в ваших текущих определениях profile->user является foreignkey, или отношением "многие-к-одному", поэтому вы допускаете несколько профилей для одного и того же пользователя. Таким образом, post.user.profiles вернет набор (даже если это набор из одного), а нам нужен только первый объект в этом наборе, следовательно, [0]. Если вам нужен только один профиль для каждого пользователя (а обычно так и бывает), измените свой
class Profile(models.Model):
""" User profile data """
user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='profile')
to
user = models.OneToOneField(
User, on_delete=models.CASCADE, related_name='profile')
затем перемигрируйте вашу базу данных. Теперь вы можете использовать
<h5 class="col-12 col-md-6">{{ post.user.profile.first_name }} {{ post.user.profile.first_name }}</h5>
Следующая проблема заключается в том, что вы будете делать вызов базы данных КАЖДЫЙ раз, когда вы получаете информацию о плакате, когда вы просматриваете ее, что будет медленно, если у вас много сообщений. Возможно, это немного дальше, чем вы уже достигли, но это полезно иметь в виду на будущее:
Вы можете сделать цикл обработки сообщений более эффективным, предварительно загрузив всю необходимую информацию в один вызов БД в вашем представлении.
Если вы сделали так, как я предложил, и сделали профиль отношением один-к-одному, тогда мы также можем использовать select_related для отслеживания плаката до его профиля и захвата информации профиля, когда мы получаем сообщение (есть foreignkey от сообщения к пользователю и one_to_one от пользователя к профилю, поэтому select_related должен работать, иначе нам нужен prefetch_related для отслеживания foreignkey)
Post.objects.all().select_related('user__profile')
Вы обращаетесь к нему точно так же в своем шаблоне - он просто захвачен более эффективно.