progammistDec. 26, 2020, 5:29 p.m.

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

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/ без дополнительных каталогов. Буду благодарен за любую помощь.

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.
10
  • Dec. 27, 2020, 10:39 a.m.
  • (edited)

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

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

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

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

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

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

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

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')

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

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

ну я бы постарался убедить бизнес, что так делать - не очень и с точки зрения всякого 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) передавал бы найденные объекты внутрь "представлений", чтобы не делать доп запросов

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

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. В чем может быть дело?

потому что в 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()    

Спасибо!

  • Dec. 28, 2020, 1:04 p.m.

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

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

я создал прилажуху 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})

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

Comments

Only authorized users can post comments.
Please, Log in or Sign up
Card image cap
Pulsum Via

Project for travelers from EVILEG.

Go
Fornex

Let me recommend you a great European Fornex hosting.

Fornex has proven itself to be a stable host over the years.

For Django projects I recommend VPS hosting

Following the link you will receive a 5% discount on shared hosting services, dedicated servers, VPS and VPN

View Hosting
Share on social networks
Donate

The EVILEG project has switched to a non-commercial basis and will develop solely on the enthusiasm of the site creator, the enthusiasm of users, donations and the hosting referral system

Thank you for your support

Available ways to support the project

PayPal

PatreonYandex.MoneyMore
s

C++ - Test 002. Constants

  • Result:50points,
  • Rating points-4
s

C++ - Test 001. The first program and data types

  • Result:66points,
  • Rating points-1
r

C++ - Test 002. Constants

  • Result:58points,
  • Rating points-2
Last comments

Qt/C++ - Lesson 061. Adding images to the application using the Drag And Drop method from the file manager

Доброго времени суток. А если нужно и изображение и текст? Что-то потерялся немного.... // Вместо отрисовки иконки и текста будем отрисовывать только одно изображение // с н…
AS

Qt/C++ - Lesson 004. QSqlTableModel – How to present the table from database?

error insert into TableExample " Количество параметров не совпадает" Я путь свой прописывала и даже бд удаляла, чтобы заново сделать, не работает. (всё остальное как у вас... Вроде ка…
i

Qt/C++ - Lesson 042. PopUp notification in the Gnome style using Qt

cialis powder comprare cialis online
LD

GameDev on Qt - Tutorial 1. Track mouse movement in QGraphicsScene

Вполне возможно, что ты не закинул graphicsView в дизайнере в виджет
LD

GameDev on Qt - Tutorial 1. Track mouse movement in QGraphicsScene

Кому интересно, поворот в slotTarget можно в одну строку организовать this->setRotation(90 + rotation() + qRadiansToDegrees(qAtan2(mapFromScene(point).y(), mapFromScene(point).x())));
Now discuss on the forum
K

Sorting the added QML elements in the ListModel

buy accutane online forum https://isotretinoinxp.top
  • Nomad
  • July 30, 2022, 5:42 a.m.

Как работать с HTMX?

Приветствую колеги. На днях наткнулся на вот это : https://htmx.org/ На офф сайте написанно вот такая фраза: htmx gives you access to AJAX, CSS Transitions, We…
h
  • harisr
  • July 25, 2022, 2:56 a.m.

QT - Native App Integration

Привет, у нас уже есть собственное приложение для Android. Можем ли мы интегрировать пользовательское представление QT в приложение со всем приложением QT внутри представления. Если да, ука…

Правильный запуск сервера на vps - Django

О я как то себе дома локальный сервер создавал. Вам же нужно просто сделать ручками конфигурацию системы. Настроить Nginx ну либо Apache (тут кому что нравится). Соответственно БД и всё остально…
o

Распознание объектов

Я к тому, что, возможно, софт уже есть.
About
Services
© EVILEG 2015-2022
Recommend hosting TIMEWEB