🐍🚀 Создаем рекрутинговый портал на Django: часть 3

В заключительной части: создание мессенджера, реализация поиска по профилям и проектам, отзывы и пагинация.
🐍🚀 Создаем рекрутинговый портал на Django: часть 3

Пятый этап

Зарегистрированные пользователи сайта ITfinder должны иметь возможность обмениваться сообщениями и оставлять отзывы о проектах. И то, и другое реализовать на Django несложно.

Мессенджер

Начнем работу с создания модели Message – сохраните этот код в users/models.py:

        class Message(models.Model):
    sender = models.ForeignKey(
        Profile, on_delete=models.SET_NULL, null=True, blank=True)
    recipient = models.ForeignKey(
        Profile, on_delete=models.SET_NULL, null=True, blank=True, related_name="messages")
    name = models.CharField(max_length=200, null=True, blank=True)
    email = models.EmailField(max_length=200, null=True, blank=True)
    subject = models.CharField(max_length=200, null=True, blank=True)
    body = models.TextField()
    is_read = models.BooleanField(default=False, null=True)
    created = models.DateTimeField(auto_now_add=True)
    id = models.UUIDField(default=uuid.uuid4, unique=True,
                        primary_key=True, editable=False)
 
    def __str__(self):
        return self.subject
 
    class Meta:
        ordering = ['is_read', '-created']

    

Подготовьте и примените миграции:

        python manage.py makemigrations
python manage.py migrate 

    

После этого добавьте импорт Message и новый класс в users/form.py:

        class MessageForm(ModelForm):
    class Meta:
        model = Message
        fields = ['name', 'email', 'subject', 'body']
 
    def __init__(self, *args, **kwargs):
        super(MessageForm, self).__init__(*args, **kwargs)
 
        for name, field in self.fields.items():
            field.widget.attrs.update({'class': 'input'})

    

Шаблоны, которые понадобятся для мессенджера:

Сохраните эти шаблоны в templates/users. Еще мы добавим ссылку на входящие в шаблоне navbar.html{% url 'inbox' %}.

Мессенджер на Django
Мессенджер на Django
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста»

Отзывы о проектах

В первой части туториала мы уже частично реализовали функциональность для отзывов о проектах. Теперь нам нужно связать отзывы с пользователями. Для этого мы внесем дополнения в модель Project:

            @property
    def reviewers(self):
        queryset = self.review_set.all().values_list('owner__id', flat=True)
        return queryset
 
    @property
    def getVoteCount(self):
        reviews = self.review_set.all()
        upVotes = reviews.filter(value='up').count()
        totalVotes = reviews.count()
 
        ratio = (upVotes / totalVotes) * 100
        self.vote_total = totalVotes
        self.vote_ratio = ratio
 
        self.save()
    

Дополним класс Review:

        class Review(models.Model):
    VOTE_TYPE = (
        ('up', 'Положительная оценка'),
        ('down', 'Отрицательная оценка'),
    )
    owner = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True)
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    body = models.TextField(null=True, blank=True)
    value = models.CharField(max_length=200, choices=VOTE_TYPE)
    created = models.DateTimeField(auto_now_add=True)
    id = models.UUIDField(default=uuid.uuid4, unique=True,
                          primary_key=True, editable=False)
 
    class Meta:
        unique_together = [['owner', 'project']]
 
    def __str__(self):
        return self.value

    

В шаблоне single-projects.html изменим ссылки на авторов отзывов: {% url 'user-profile' review.owner %}, после чего добавим форму отзыва в projects/forms.py:

        class ReviewForm(ModelForm):
    class Meta:
        model = Review
        fields = ['value', 'body']
 
        labels = {
            'value': 'Оцените проект',
            'body': 'Добавьте комментарий'
        }
 
    def __init__(self, *args, **kwargs):
        super(ReviewForm, self).__init__(*args, **kwargs)
 
        for name, field in self.fields.items():
            field.widget.attrs.update({'class': 'input'})


    

Осталось внести небольшие изменения в представление проекта в файле projects/views.py. Добавим import ReviewForm и код:

        def project(request, project_slug):
   project = Project.objects.get(slug=project_slug)
   tags = project.tags.all()
   form = ReviewForm()
   if request.method == 'POST':
       form = ReviewForm(request.POST)
       review = form.save(commit=False)
       review.project = project
       review.owner = request.user.profile
       review.save()
       project.getVoteCount
       messages.success(request, 'Ваш отзыв был добавлен!')
       return redirect('project', project_slug=project.slug)   
   return render(request, 'projects/single-project.html', {'project': project, 'form': form})


    

Готово, теперь на сайте есть система отзывов:

Система комментариев на Django
Система комментариев на Django

Шестой этап

На заключительном этапе мы сделаем пагинацию профилей и проектов, а затем реализуем систему поиска.

Пагинация

Сначала сделаем пагинацию для вывода проектов. Создайте файл projects/utils.py, а в файл projects/views.py добавьте импорт:

        from django.core import paginator
from .utils import paginateProjects

    

В конец шаблона projects.html добавьте:

        {% include 'pagination.html' with queryset=projects custom_range=custom_range %}
    

После этого в папку с глобальными шаблонами положите файл pagination.html.

Запустите сервер и проверьте: проекты теперь выводятся постранично.

Таким же образом сделаем пагинацию для постраничного вывода профилей в приложении Users:

Готово – теперь профили выводятся по 6 штук на странице:

Пагинация
Пагинация

Поиск

Мы реализуем поиск по проектам и профилям с помощью модуля Q. Сначала добавим импорт from django.db.models import Q и эту функцию в projects/utils.py:

         def searchProjects(request):
     search_query = ''
     if request.GET.get('search_query'):
        search_query = request.GET.get('search_query')
     tags = Tag.objects.filter(name__icontains=search_query)
 
    projects = Project.objects.distinct().filter(
        Q(title__icontains=search_query) |
        Q(description__icontains=search_query) |
        Q(owner__name__icontains=search_query) |
        Q(tags__in=tags)
    )
    return projects, search_query
    

Использование модуля Q значительно изменит представление для вывода проектов:

        def projects(request):
    projects, search_query = searchProjects(request)
    custom_range, projects = paginateProjects(request, projects, 6)
 
    context = {'projects': projects,
            'search_query': search_query, 'custom_range': custom_range}
    return render(request, 'projects/projects.html', context)


    

Вызов Q в шаблоне projects.html производит {{ search_query }}. Все готово – поиск по проектам работает:

Результаты поиска по запросу "магазин"
Результаты поиска по запросу "магазин"

То же самое сделаем для поиска по профилям. Добавим функцию поиска в users/utils.py:

        def searchProfiles(request):
    search_query = ''
    if request.GET.get('search_query'):
        search_query = request.GET.get('search_query')
 
    skills = Skill.objects.filter(name__icontains=search_query)
    profiles = Profile.objects.distinct().filter(
        Q(name__icontains=search_query) |
        Q(short_intro__icontains=search_query) |
        Q(skill__in=skills)
    )
    return profiles, search_query
    

Изменим представление для вывода профилей в users/views.py:

        def profiles(request):
    profiles, search_query = searchProfiles(request)
 
    custom_range, profiles = paginateProfiles(request, profiles, 6)
    context = {'profiles': profiles, 'search_query': search_query,
            'custom_range': custom_range}
    return render(request, 'users/profiles.html', context)


    

И добавим {{ search_query }} в profiles.html. Готово – поиск по профилям работает:

Результаты поиска по запросу "Python"
Результаты поиска по запросу "Python"

На этом разработка сайта завершена. Код готового проекта можно взять в репозитории ITfinder.

***

Материалы по теме


Комментарии

 
05 декабря 2022

Наталья, добрый день. У меня возникли проблемы с подсчетом голосов и положительных оценок. На сайте показывает что отзыв добавлен, в админке все это видно, но в базу не добавляет новых значений и соответсвенно на сайте так все и остаться по "0". Вы сможете подсказать куда копать? Или можно с вами связаться через другой канал связи, чтобы была возможность код показать ?

06 декабря 2022

Добрый день, Алексей. Напишите в ТГ - natkaida, или на ВК - natalia.kaida. Или загрузите свой код на Гитхаб, если так удобнее.

12 ноября 2022

Наталья, есть еще вопрос: На гитхабе в файлике projects.html, 31 строчка: Мы разве в маршрут 'project' не должны пробрасывать не слаг а uuid? Тоесть вот так project.id? Потому что со слагом джанго выкидывает ошибку мол он ожидал uuid. Но при таком раскладе смысл slug ов теряется т.к. в строке появляется uuid. Либо я что то не доделал. Подскажите плз

13 ноября 2022

Может, не добавили маршрут в urls?

13 ноября 2022

Да, надо было изменить немного. На первых этапах тут на сайте разногласия небольшие с кодом который на гитхабе в последнем шаге step 6. Спасибо за помощь !)

13 ноября 2022

Всегда рада:). С большим проектом иногда можно увлечься и накодить чуть больше, чем освещено в статье. Но код по каждому этапу в отдельности всегда железно работает - если что-то глючит, можно скопировать весь этап сразу, и все заработает:).

10 ноября 2022

Спасибо за статью. Подскажите, мы в этот проект куда-нибудь асинхронку можем добавить? Если да, то куда?

10 ноября 2022

В этот проект я добавила бы async/await на стороне фронтенда. То есть асинхронная функциональность была бы реализована на JS.

10 ноября 2022

Спасибо

 

Подскажите пожалуйста, на гитхаб сейчас в main.js - только заготовка c fetch для этого? html с api/remove-tag не добавляли или я не там ищу и как использовать let tags = document.getElementsByClassName('course-tag') и блок кода под ним?

Ага, заготовка. Допилю на досуге :)

ВАКАНСИИ

Добавить вакансию
Senior Software Engineer (Java)
от 4500 USD до 6000 USD
Ведущий SRE инженер
Москва, по итогам собеседования
Senior MLE (SE)
от 5000 USD до 9000 USD

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

LIVE >

Подпишись

на push-уведомления