progammist
progammist26 декабря 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
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

10
Nomad
  • 27 декабря 2020 г. 10:39
  • (ред.)

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

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

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

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

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

    progammist
    • 27 декабря 2020 г. 16:28

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

      progammist
      • 27 декабря 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 декабря 2020 г. 3:48
        • (ред.)

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

          progammist
          • 28 декабря 2020 г. 5:20

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

            Илья Чичак
            • 28 декабря 2020 г. 5: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 декабря 2020 г. 6:23
              • (ред.)

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

              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 декабря 2020 г. 6:31
                • (ред.)

                потому что в 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 декабря 2020 г. 6:51

                  Спасибо!

                    Nomad
                    • 28 декабря 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})
                    

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

                      Комментарии

                      Только авторизованные пользователи могут публиковать комментарии.
                      Пожалуйста, авторизуйтесь или зарегистрируйтесь
                      Дмитрий

                      C++ - Тест 004. Указатели, Массивы и Циклы

                      • Результат:60баллов,
                      • Очки рейтинга-1
                      Дмитрий

                      C++ - Тест 003. Условия и циклы

                      • Результат:92баллов,
                      • Очки рейтинга8
                      d
                      • dsfs
                      • 26 апреля 2024 г. 4:56

                      C++ - Тест 004. Указатели, Массивы и Циклы

                      • Результат:80баллов,
                      • Очки рейтинга4
                      Последние комментарии
                      k
                      kmssr8 февраля 2024 г. 18:43
                      Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                      АК
                      Анатолий Кононенко5 февраля 2024 г. 1:50
                      Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                      EVA
                      EVA25 декабря 2023 г. 10:30
                      Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
                      J
                      JonnyJo25 декабря 2023 г. 8:38
                      Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
                      G
                      Gvozdik18 декабря 2023 г. 21:01
                      Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
                      Сейчас обсуждают на форуме
                      G
                      George137 мая 2024 г. 0:27
                      добавить qlineseries в функции в функции: "GPlotter::addSeries(QString title, QVector &arr)" я вызываю метод setChart(...), я в конструктор передал адрес на QChartView элемент
                      BlinCT
                      BlinCT5 мая 2024 г. 5:46
                      Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
                      PS
                      Peter Son3 мая 2024 г. 17:57
                      Best Indian Food Restaurant In Cincinnati OH Ready to embark on a gastronomic journey like no other? Join us at App india restaurant and discover why we're renowned as the Best Indian Food Restaurant In Cincinnati OH . Whether y…
                      Evgenii Legotckoi
                      Evgenii Legotckoi2 мая 2024 г. 14:07
                      Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
                      IscanderChe
                      IscanderChe30 апреля 2024 г. 4:22
                      Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…

                      Следите за нами в социальных сетях