Всем привет. Помогите советом. Проходил очередное тестовое задание и наконец дали фидбэк. Обычно сначала давали тестовое задание, а потом приглашали на интервью, при этом про возможные ошибки в тестовом задании ни слова, а тут наоборот все получилось)
Задание:
Имеется стандартная база пользователей (добавляются через админку, регистрацию делать не нужно). У каждого пользователя есть блог (пара базовых полей). Нужно, чтобы у пользователей была возможность смотреть записи других пользователей (которые опубликованы), чтобы пользователь мог добавлять и редактировать свои записи. На всех страницах должна требоваться авторизация. В качестве дизайна можно использовать Bootstrap, но и можно использовать plain HTML.
https://github.com/igrik6556/icemint_task - вот репозиторий
Рекомендовали выполнить это задание на django 1.10, но сообщили это уже после того, когда я сделал на 1.8)) Отсюда вопрос, стоит ли гнаться за новыми версиями, как только они выйдут, ведь многие приложения не работают с новыми версиями django. И на каких версиях обычно предпочитают в компаниях разрабатывать?
1) Страница вывода всех постов пользователя. Сделал первоначально так:
models.py
class Post(models.Model):
class Meta:
db_table = "Post"
ordering = ["-dt_create"]
verbose_name = _("Post")
verbose_name_plural = _("Posts")
title = models.CharField(
_("Entry title"),
max_length=100
)
text = models.TextField(_("Entry text"))
dt_create = models.DateTimeField(
_("Creation time"),
default=timezone.now
)
dt_edit = models.DateTimeField(
_("Last edit"),
null=True,
blank=True
)
is_publish = models.BooleanField(_("Publish?"))
author = models.ForeignKey(
User,
verbose_name=_("Entry author")
)
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if self.pk:
self.dt_edit = timezone.now()
super(Post, self).save(*args, **kwargs)
def get_user_post(self):
return Post.objects.filter(author=self).filter(is_publish=True)
def get_my_post(self):
return Post.objects.filter(author=self)
Первоначальная вьюха:
class UserPosts(ListView):
"""
Display all records that the user has left.
"""
template_name = "blog/posts_list.html"
model = Post
page_kwarg = "usr_pk"
def get_context_data(self, **kwargs):
context = super(UserPosts, self).get_context_data(**kwargs)
if int(self.request.user.id) == int(self.kwargs["usr_pk"]):
context["posts"] = Post.get_my_post(self.request.user.id)
else:
context["posts"] = Post.get_user_post(self.kwargs["usr_pk"])
context["auth"] = User.objects.get(pk=self.kwargs["usr_pk"])
return context
В итоге прислали такое замечание:
Для постов автора обычно создается ForeignKey (это есть) и стоит указать related_name="posts", например. После этого из модели User можно получать все посты пользователя (user.posts.all()) и не прописывать обертки вида get_user_post и get_my_post
class UserPosts. Описание класса вообще никакой роли не играет (все данные передаются через контекст), хотя это должно быть основой в шаблоне. Стоит либо переопределять функцию get_queryset(), либо указывать в качестве модели User и в списке записей проходить через object.posts.all()
И я додумался только до такого исправления, но по сути я перенес все из get_context_data в get_queryset. Может подскажете как можно сделать красивее?
class UserPosts(ListView):
"""
Display all records that the user has left.
"""
template_name = "blog/post_list.html"
context_object_name = "posts"
model = Post
def get_queryset(self, **kwargs):
qs = super(UserPosts, self).get_queryset(**kwargs)
if self.request.user.id == int(self.kwargs["pk"]):
return qs.filter(author=self.kwargs["pk"])
else:
return qs.filter(author=self.kwargs["pk"]).filter(is_publish=True)
def get_context_data(self, **kwargs):
context = super(UserPosts, self).get_context_data(**kwargs)
context["author"] = User.objects.get(pk=self.kwargs["pk"])
return context
2) Для поля dt_edit нужно models.DateTimeField(auto_now_add=False, auto_now=True)
Вот тут я не понял. Я хотел чтобы поле dt_edit записывалось только при первом редактировании записи, а не при ее создании. Поэтому сделал это в методе save. А запись dt_edit = models.DateTimeField(auto_now_add=False, auto_now=True) присваивает время при создании записи.
3) Сейчас уже не стоит использовать функцию login_required(), а стоит использовать миксин LoginRequiredMixin. Это делается потому что в будущем если нужны будут какие-то специальные условия для авторизации, изменять их можно было максимально просто. А функция login_required() не имеет никаких дополнительных возможностей
Опять таки если я правильно понимаю, в django 1.8 нету этого миксина. Он появился только начиная с 1.9. Поэтому я и оставил login_required
4) Права в EditPost стоит проверять через PermissionRequiredMixin. В этом случае будет проводиться валидация ещё на уровне запроса, а не при отправке формы.
Тут с миксином та же ситуация что и в предыдущем вопросе. Но я исправил представление, чтобы проверялась форма на уровне запроса. Все работает, но я не уверен что я правильно это сделал, исправьте пожалуйста если что.
Было
class EditPost(SuccessMessageMixin, UpdateView):
model = Post
form_class = PostForm
template_name = "blog/edit_post.html"
success_message = _("Post <%(title)s> successfully updated!")
pk_url_kwarg = "post_pk"
def form_valid(self, form):
if form.instance.author != self.request.user:
return HttpResponseForbidden()
return super(EditPost, self).form_valid(form)
Стало
class EditPost(SuccessMessageMixin, UpdateView):
model = Post
form_class = PostForm
template_name = "blog/post_form.html"
success_message = _("Post <%(title)s> successfully updated!")
def dispatch(self, request, *args, **kwargs):
post = Post.objects.get(pk=kwargs["pk"])
if post.author != self.request.user:
raise Http404
return super(EditPost, self).dispatch(request, *args, **kwargs)
С остальными замечаниями я разобрался, а насчет этих хотел обсудить с опытными программистами на этом форуме. Заранее спасибо
ЗЫ может кто нибудь посмотрит другие мои проэты в репозитории и укажет мне еще какие-нибудь ошибки. Буду очень признателен. Спасибо