progammist
Dec. 27, 2020, 4:29 a.m.

Как вывести статьи и профиль

Django, python, slug

Всем привет!

Вопрос такой, у меня есть мини-блог, есть статьи и профиль(страницы) пользователей.
Статьи я вывожу по адресу site/названиестатьи
Кабинет пользователя я хочу вывести по адресу site/юзернейм

views.py (статьей):

  1. def post_detail(request, slug):
  2. post = get_object_or_404(Post, slug=slug)
  3. return render(request, 'post_detail.html', {'post': post })

views.py (пользователей):

  1. class UserProfileView(DetailView):
  2. template_name = 'users/profile/profile-user-view.html'
  3. queryset = User.objects.all()
  4.  
  5. def get_object(self):
  6. username = self.kwargs.get("username")
  7. return get_object_or_404(User, username=username)

URL's:

  1. path('<str:username>', UserProfileView.as_view(), name='user_detail')
  2. path('<slug:slug>', views.post_detail, name='post_detail')

Сейчас у меня открывается профиль пользователя по адресу site/юзернейм, но не открывается статья по адресу site/названиестатьи. Ошибка:

  1. Page not found (404)
  2. Request URL: http://127.0.0.1:8000/testarcticle
  3. Raised by: users.views.UserProfileView
  4. No User matches the given query.

Подозреваю какой-то конфлитк в урл (slug). Как мне сделать так, чтобы откывалось и статьи и кабинет по адресу site/ без дополнительных каталогов. Буду благодарен за любую помощь.

3

Do you like it? Share on social networks!

10
Nomad
  • Dec. 27, 2020, 9:39 p.m.
  • (edited)

решение сходу не подскажу (завтра опишу мое решение когдато сделанное) НО при создании урл-ов в джанго Вы должны иметь в виду следуещее (возьмите как правило):

когда браузер запрашивает какую то страницу, джанго идет в файл урлс.пу и начинаест искать соответствие и как тока находит первое соответствие он останавливается и запускает то представление в первом найденном урле, дальше ему похер

в вашем случае браузер просит у бэка страницу, урл которой, выглядет: <домен>/<что тот>
у вас два path-а которые, соответствуют данному шаблону, НО, этот: path(' ', UserProfileView.as_view(), name='user_detail') идет первым, джанго находит его и отдает представление UserProfileView браузеру

теперь вы как кодер знаете что у вас есть 2 разных урла - типа разных, джанго пофиг что они разные

если вы в виде экперимета поменяете местами эти 2 path-а, то при запросе типа <домен>/<что тот>, будет выполнятся path(' ', views.post_detail, name='post_detail') и не важно что вы запросите профиль или статейку он все равно попытается обратится к представлению post_detail а представление UserProfileView будет игнорированно!

    progammist
    • Dec. 28, 2020, 3:28 a.m.

    Спасибо, я понял намёк, что надо выразить проверку в одном представлении, и проверять по одному урлу, но не знаю как выразить это в синтаксисе

      progammist
      • Dec. 28, 2020, 8:03 a.m.

      Кажется, разобрался:

      1. def two_href(request, path_string):
      2.  
      3. username = User.objects.filter(username=path_string)
      4. slug = Post.objects.filter(slug=path_string)
      5.  
      6. if username:
      7. ...
      8. elif slug:
      9. ....
      10. else:
      11. raise Http404("404 error")

      Url:

      1. path('<str:path_string>', views.two_href, name='two_href')
        Илья Чичак
        • Dec. 28, 2020, 2:48 p.m.
        • (edited)

        я бы сделал site/a/article_name и site/u/username - куда проще и лишних запросов не делать
        плюс логика относительно каждого запроса изолированна

          progammist
          • Dec. 28, 2020, 4:20 p.m.

          предаставим, что по бизнес-логике url должно быть именно site/article и site/username, как бы вы поступили?)

            Илья Чичак
            • Dec. 28, 2020, 4:54 p.m.

            ну я бы постарался убедить бизнес, что так делать - не очень и с точки зрения всякого seo и с точки зрения консистентности данных. Что это накладывает ненужные ограничения и на article и на username (слабо связанные сущности начинают оч плотно зависеть друг от друга и накладывать ограничения - чистая архитектура вышла из чата)

            плюс начинаются проблемы, если на этих страницах есть формы (допустим, на странице профиля форма его изменения, а на странице статьи - форма комментария) - представление становится слишком большим и выполнять слишком много (SOLID и KISS - вышли вслед за архитектурой)

            ну и самая засада начнется, если придется делать разные вложенные страницы - вот тогда будет прям больно: site/ profile /edit + site/ atricle /edit

            в этом плане, мне кажется, было бы куда проще сделать так:

            1. path('p/<str:username>/', include=[
            2. path('', view_profile),
            3. path('edit/', edit_profile),
            4. ...
            5. ]),
            6. path('p/<slug:slug>/', include=[
            7. path('', view_article),
            8. path('edit/', edit_article),
            9. ...
            10. ]),

            НО, из академического интереса, я бы сделал так:

            разделил обработку каждой страницы через helper функцию

            1. def post_page_view(request, post_obj):
            2. # code
            3.  
            4. def profile_page_view(request, user_obj):
            5. # code
            6.  
            7. def page_processor(request, path_str):
            8. post_obj = Post.objects.filter(slug=path_str).first()
            9. if post_obj:
            10. return post_page_view(request, post_obj)
            11. user_obj = User.objects.filter(username=path_str).first()
            12. if user_obj:
            13. return profile_page_view(request, user_obj)
            14. raise Http404()
            15.  

            я бы сделал так потому, что:
            1) поле slug создается с индексом и искать через filter по нему - очень быстро и не накладно
            2) post_page_view и profile_page_view - устроены точно как представления, так что в случае чего их можно будет легко изолировать с минимумом изменений
            3) передавал бы найденные объекты внутрь "представлений", чтобы не делать доп запросов

              progammist
              • Dec. 28, 2020, 5:23 p.m.
              • (edited)

              Спасибо за развернутый ответ, изменил по вашему примеру:

              views.py

              1.  
              2. def profile_page_view(request, user_obj):
              3.  
              4. if user_obj:
              5. user = get_object_or_404(User, username=user_obj)
              6. else:
              7. user = request.user
              8. args = {'user': user}
              9. return render(request, 'users/profile/profile-user-view.html', args)
              10.  
              11.  
              12.  
              13. def post_page_view(request, post_obj):
              14.  
              15. post = get_object_or_404(Post, slug=post_obj)
              16.  
              17.  
              18. return render(request, 'post_detail.html', {'post': post, })
              19.  
              20. def page_processor(request, path_string):
              21. post_obj = Post.objects.filter(slug=path_string).first()
              22. if post_obj:
              23. return post_page_view(request, post_obj)
              24. user_obj = User.objects.filter(username=path_string).first()
              25. if user_obj:
              26. return profile_page_view(request, user_obj)
              27. raise Http404()
              28.  
              29.  
              30.  

              URL:

              1. path('<str:path_string>', blog_views.page_processor, name='post_detail')

              На страницу юзера заходит без проблем, но на страницу статьи, почему-то 404. В чем может быть дело?

                Илья Чичак
                • Dec. 28, 2020, 5:31 p.m.
                • (edited)

                потому что в post_page_view и profile_page_view вы передаете уже объект

                надо:

                1. def profile_page_view(request, user_obj):
                2. return render(request, 'users/profile/profile-user-view.html', {'user': user})
                3.  
                4.  
                5. def post_page_view(request, post_obj):
                6. return render(request, 'post_detail.html', {'post': post, })
                7.  
                8.  
                9. def page_processor(request, path_string):
                10. post_obj = Post.objects.filter(slug=path_string).first()
                11. if post_obj:
                12. return post_page_view(request, post_obj)
                13. user_obj = User.objects.filter(username=path_string).first()
                14. if user_obj:
                15. return profile_page_view(request, user_obj)
                16. raise Http404()
                17.  
                  progammist
                  • Dec. 28, 2020, 5:51 p.m.

                  Спасибо!

                    Nomad
                    • Dec. 29, 2020, 12:04 a.m.

                    как и обещал, я опишу некогда решоную мной задачу, немного похоже на Вашу с урлами.

                    надо было создать меню, у урлы для меню должны были выглядеть <домен>/<слаг>

                    я создал прилажуху pages, в этой прилажухи была одна модель:

                    1. class Page(models.Model):
                    2. """Models pages"""
                    3.  
                    4. page_title = models.CharField(_("Titlu"), max_length=500)
                    5. page_sub_title = models.CharField(_("SubTitlu"), max_length=500, blank=True, null=True)
                    6. page_text = models.TextField(_("Text"), blank=True, null=True)
                    7. edit_date = models.DateTimeField(_("Data redactarii"), auto_now=True, blank=True, null=True)
                    8. page_published_date = models.DateTimeField(_("Data publicarii"), blank=True, null=True)
                    9. page_published = models.BooleanField(_("Publicat?"), default=True)
                    10. page_template = models.CharField(_("Template"), max_length=500, default="pages/home.html")
                    11. page_registration_required = models.BooleanField(
                    12. _('Need registration'),
                    13. help_text=_("Daca sta bifa atunci doar utilizatorii inregistrati pot vedea pagina."),
                    14. default=False,
                    15. )
                    16. page_slug = models.CharField("url", max_length=100, unique=True)
                    17.  
                    18. def __str__(self):
                    19. return self.page_title
                    20.  
                    21. def save(self, *args, **kwargs):
                    22. if self.page_slug is None:
                    23. self.page_slug = "/"
                    24. if not f"{self.page_slug}".startswith("/"):
                    25. self.page_slug = "/" + self.page_slug
                    26. if not self.page_slug.endswith("/"):
                    27. self.page_slug += "/"
                    28. super().save(*args, **kwargs)
                    29.  
                    30. def get_absolute_url(self):
                    31. return iri_to_uri(get_script_prefix().rstrip('/') + self.page_slug)

                    как видно, некоторые страницы были доступны только авторизованным пользователям.

                    пыть к любой странице данной модели вот такой:

                    path(' ', get_page, name="page"),

                    дальше в краце:
                    1. создал middleware.py и добавил в файл сеттингс:

                    1. class PageFallbackMiddleware(MiddlewareMixin):
                    2. def process_response(self, request, response):
                    3. if response.status_code != 404:
                    4. return response
                    5. try:
                    6. return get_page(request, request.path_info)
                    7. except Http404:
                    8. return response
                    9. except Exception:
                    10. if settings.DEBUG:
                    11. raise
                    12. return response
                    1. views.py вот такой:
                    1. def get_page(request, url):
                    2. """page on url"""
                    3.  
                    4. print(url)
                    5. if not url.startswith('/'):
                    6. url = '/' + url
                    7. try:
                    8. page = get_object_or_404(Page, page_slug=url, page_published=True)
                    9. except Http404:
                    10. if not url.endswith('/') and settings.APPEND_SLASH:
                    11. url += '/'
                    12. page = get_object_or_404(Page, page_slug=url, page_published=True)
                    13. return HttpResponsePermanentRedirect('%s/' % request.path)
                    14. else:
                    15. raise
                    16.  
                    17. return render_page(request, page)
                    18.  
                    19.  
                    20. @csrf_protect
                    21. def render_page(request, page):
                    22. """Page render"""
                    23.  
                    24. if page.page_registration_required and not request.user.is_authenticated:
                    25. from django.contrib.auth.views import redirect_to_login
                    26. return redirect_to_login(request.path)
                    27. return render(request, page.page_template, {"page": page})

                    будет время я обьясню код

                      Comments

                      Only authorized users can post comments.
                      Please, Log in or Sign up
                      • Last comments
                      • AK
                        April 1, 2025, 11:41 a.m.
                        Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
                      • Evgenii Legotckoi
                        March 9, 2025, 9:02 p.m.
                        К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
                      • VP
                        March 9, 2025, 4:14 p.m.
                        Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
                      • ИМ
                        Nov. 22, 2024, 9:51 p.m.
                        Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                      • Evgenii Legotckoi
                        Oct. 31, 2024, 11:37 p.m.
                        Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup