🐍🚀 Создаем рекрутинговый портал на 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' %}
.
Отзывы о проектах
В первой части туториала мы уже частично реализовали функциональность для отзывов о проектах. Теперь нам нужно связать отзывы с пользователями. Для этого мы внесем дополнения в модель 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})
Готово, теперь на сайте есть система отзывов:
Шестой этап
На заключительном этапе мы сделаем пагинацию профилей и проектов, а затем реализуем систему поиска.
Пагинация
Сначала сделаем пагинацию для вывода проектов. Создайте файл 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:
- создадим файл utils.py;
- добавим изменения в шаблон profiles.html;
- внесем дополнения в users/views.py.
Готово – теперь профили выводятся по 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. Готово – поиск по профилям работает:
На этом разработка сайта завершена. Код готового проекта можно взять в репозитории ITfinder.
Материалы по теме
- 🐍🚀 Django с нуля. Часть 1: пишем многопользовательский блог для клуба любителей задач Python
- 🐍🚀 Django с нуля. Часть 2: регистрация, авторизация, ограничение доступа
- 🐍🚀 Django с нуля. Часть 3: создание профилей, сжатие изображений, CRUD и пагинация