progammist
progammist26. Dezember 2020 17:29

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

Django, python, slug

Всем привет!

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

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

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

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

class UserProfileView(DetailView):
    template_name = 'users/profile/profile-user-view.html'
    queryset = User.objects.all()

    def get_object(self):
        username = self.kwargs.get("username")
        return get_object_or_404(User, username=username)

URL's:

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

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

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

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

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

10
Nomad
  • 27. Dezember 2020 10:39
  • (bearbeitet)

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

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

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

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

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

    progammist
    • 27. Dezember 2020 16:28

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

      progammist
      • 27. Dezember 2020 21:03

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

      def two_href(request, path_string):
      
              username = User.objects.filter(username=path_string)
              slug = Post.objects.filter(slug=path_string)
      
              if username:
                  ...       
              elif slug:   
                  ....    
              else:
                  raise Http404("404 error")
      

      Url:

      path('<str:path_string>', views.two_href, name='two_href')
      
        Илья Чичак
        • 28. Dezember 2020 03:48
        • (bearbeitet)

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

          progammist
          • 28. Dezember 2020 05:20

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

            Илья Чичак
            • 28. Dezember 2020 05:54

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

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

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

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

            path('p/<str:username>/', include=[
                path('', view_profile),
                path('edit/', edit_profile),
                ...
            ]),
            path('p/<slug:slug>/', include=[
                path('', view_article),
                path('edit/', edit_article),
                ...
            ]),
            

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

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

            def post_page_view(request, post_obj):
                # code
            
            def profile_page_view(request, user_obj):
                # code
            
            def page_processor(request, path_str):
                post_obj = Post.objects.filter(slug=path_str).first()
                if post_obj:
                    return post_page_view(request, post_obj)
                user_obj = User.objects.filter(username=path_str).first()
                if user_obj:
                    return profile_page_view(request, user_obj)
                raise Http404()
            
            

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

              progammist
              • 28. Dezember 2020 06:23
              • (bearbeitet)

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

              views.py

              
              def profile_page_view(request, user_obj):
              
                  if user_obj:
                      user = get_object_or_404(User, username=user_obj)
                  else:
                      user = request.user
                  args = {'user': user}
                  return render(request, 'users/profile/profile-user-view.html', args)
              
              
              
              def post_page_view(request, post_obj):
              
                  post = get_object_or_404(Post, slug=post_obj)
              
              
                  return render(request, 'post_detail.html', {'post': post, })
              
              def page_processor(request, path_string):
                post_obj = Post.objects.filter(slug=path_string).first()
                if post_obj:
                   return post_page_view(request, post_obj)
                user_obj = User.objects.filter(username=path_string).first()
                if user_obj:
                  return profile_page_view(request, user_obj)
                raise Http404()    
              
              
              
              

              URL:

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

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

                Илья Чичак
                • 28. Dezember 2020 06:31
                • (bearbeitet)

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

                надо:

                def profile_page_view(request, user_obj): 
                    return render(request, 'users/profile/profile-user-view.html', {'user': user})
                
                
                def post_page_view(request, post_obj):
                    return render(request, 'post_detail.html', {'post': post, })
                
                
                def page_processor(request, path_string):
                  post_obj = Post.objects.filter(slug=path_string).first()
                  if post_obj:
                     return post_page_view(request, post_obj)
                  user_obj = User.objects.filter(username=path_string).first()
                  if user_obj:
                    return profile_page_view(request, user_obj)
                  raise Http404()    
                
                
                  progammist
                  • 28. Dezember 2020 06:51

                  Спасибо!

                    Nomad
                    • 28. Dezember 2020 13:04

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

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

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

                    class Page(models.Model):
                        """Models pages"""
                    
                        page_title = models.CharField(_("Titlu"), max_length=500)
                        page_sub_title = models.CharField(_("SubTitlu"), max_length=500, blank=True, null=True)
                        page_text = models.TextField(_("Text"), blank=True, null=True)
                        edit_date = models.DateTimeField(_("Data redactarii"), auto_now=True, blank=True, null=True)
                        page_published_date = models.DateTimeField(_("Data publicarii"), blank=True, null=True)
                        page_published = models.BooleanField(_("Publicat?"), default=True)
                        page_template =  models.CharField(_("Template"), max_length=500, default="pages/home.html")
                        page_registration_required = models.BooleanField(
                            _('Need registration'),
                            help_text=_("Daca sta bifa atunci doar utilizatorii inregistrati pot vedea pagina."),
                            default=False,
                        )
                        page_slug = models.CharField("url", max_length=100, unique=True)
                    
                        def __str__(self):
                            return self.page_title
                    
                        def save(self, *args, **kwargs):
                            if self.page_slug is None:
                                self.page_slug = "/"
                            if not f"{self.page_slug}".startswith("/"):
                                self.page_slug = "/" + self.page_slug
                            if not self.page_slug.endswith("/"):
                                self.page_slug += "/"
                            super().save(*args, **kwargs)
                    
                        def get_absolute_url(self):
                            return iri_to_uri(get_script_prefix().rstrip('/') + self.page_slug)
                    

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

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

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

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

                    class PageFallbackMiddleware(MiddlewareMixin):
                        def process_response(self, request, response):
                            if response.status_code != 404:
                                return response
                            try:
                                return get_page(request, request.path_info)
                            except Http404:
                                return response
                            except Exception:
                                if settings.DEBUG:
                                    raise
                                return response
                    
                    1. views.py вот такой:
                    def get_page(request, url):
                        """page on url"""
                    
                        print(url)
                        if not url.startswith('/'):
                            url = '/' + url
                        try:
                            page = get_object_or_404(Page, page_slug=url, page_published=True)
                        except Http404:
                            if not url.endswith('/') and settings.APPEND_SLASH:
                                url += '/'
                                page = get_object_or_404(Page, page_slug=url, page_published=True)
                                return HttpResponsePermanentRedirect('%s/' % request.path)
                            else:
                                raise
                    
                        return render_page(request, page)
                    
                    
                    @csrf_protect
                    def render_page(request, page):
                        """Page render"""
                    
                        if page.page_registration_required and not request.user.is_authenticated:
                            from django.contrib.auth.views import redirect_to_login
                            return redirect_to_login(request.path)
                        return render(request, page.page_template, {"page": page})
                    

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

                      Kommentare

                      Nur autorisierte Benutzer können Kommentare posten.
                      Bitte Anmelden oder Registrieren
                      Letzte Kommentare
                      ИМ
                      Игорь Максимов5. Oktober 2024 07:51
                      Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
                      d
                      dblas55. Juli 2024 11:02
                      QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                      k
                      kmssr8. Februar 2024 18:43
                      Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                      Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                      EVA
                      EVA25. Dezember 2023 10:30
                      Boost - statisches Verknüpfen im CMake-Projekt unter Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
                      Jetzt im Forum diskutieren
                      J
                      JacobFib17. Oktober 2024 03:27
                      добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
                      JW
                      Jhon Wick1. Oktober 2024 15:52
                      Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
                      КГ
                      Кирилл Гусарев27. September 2024 09:09
                      Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
                      F
                      Fynjy22. Juli 2024 04:15
                      при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

                      Folgen Sie uns in sozialen Netzwerken