Strona główna Polish Python Coders Group
   Strona główna   Pomoc Zaloguj się Rejestracja  
Witamy, Gość. Zaloguj się lub zarejestruj.
Czy dotarł do Ciebie email aktywacyjny?

Zaloguj się podając nazwę użytkownika, hasło i długość sesji

Aktualności: PyData Warsaw 2017, 19-20 października - konferencja Data Science
Szukaj Szukaj
Strony: [1] 2   Do dołu
Drukuj
Wątek: [DJANGO] - rozbudowane widoki  (Przeczytany 1624 razy)
« : 12:26 09/01/17 »
jundymek Offline
Advanced Python User

Zobacz profil
**

Reputacja: 3
Wiadomości: 286


W jaki sposób zabieracie się do tworzenia widoków. Próbuję napisać trochę bardziej skomplikowaną aplikację i zastanawiam się czy nie przesadzam z pakowaniem kodu do widoków. Jeden widok zrobił się naprawdę spory, ale nie mam do końca pomysłu jak to wszystko poskracać. Część kodu przerzuciłem do osobnego pliku, ale i tak wyszło tego sporo. Na chwilę obecną mam coś takiego:
Kod
from django.http import Http404
from django.shortcuts import render
from django.contrib import messages
from .models import Category, SubCategory, Site
from .forms import SiteAddForm, SiteAddFormFull
from .calculations import AddNewSite
 
def subcategory(request, category_name_slug, subcategory_name_slug):
   '''
   Wyswietlanie stron w podkategoriach i mozliwoscia dodania nowej strony
   '
''
   context = {}
   # formularz dodajacy strone na stronie podkategorii
   form = SiteAddForm(initial={'url': 'http://'})
   context['form'] = form
   try:
       category = Category.objects.get(slug=category_name_slug)
       subcategory = SubCategory.objects.filter(category=category
                                                ).get(slug=subcategory_name_slug)
       sites = Site.objects.filter(subcategory=subcategory)
       context['subcategory'] = subcategory
       context['category'] = category
       context['sites'] = sites
   except (SubCategory.DoesNotExist, Category.DoesNotExist):
       raise Http404("Nie ma takiej strony")
   if request.method == 'POST':
       form = SiteAddForm(request.POST)
       if form.is_valid():
           siteurl = form.cleaned_data['url']
           context['siteurl'] = siteurl
           try:
               page = AddNewSite(siteurl)
           except:
               print('ERRROR')
               context['Error'] = "Adres nie jest poprawny lub strona nie \
               odpowiada"

               return render(request, 'mainapp/subcategory.html', context)
           title = page.title()
           description = page.description()
           keywords = page.keywords()
           if page.check_url_in_database():
               context['Error'] = "Taka strona istnieje w bazie"
               return render(request, 'mainapp/subcategory.html', context)
           else:
               # formularz rozwiniety ze wszystkimi polami
               form_extended = SiteAddFormFull(initial={'url': siteurl, 'name':
                                                        title, 'description':
                                                        description, 'keywords':
                                                        keywords})
               context['form_extended'] = form_extended
               if request.method == 'POST':
                   form_extended = SiteAddFormFull(request.POST)
                   if form_extended.is_valid():
                       keywords = form_extended.cleaned_data['keywords']
                       name = form_extended.cleaned_data['name']
                       description = form_extended.cleaned_data['description']
                       page.add_site(category, subcategory, keywords,
                                     description, name, siteurl)
                       context['added_site'] = 'Dziękujemy za dodanie strony'
                       messages.add_message(request, messages.SUCCESS, 'Dodano!!')
 
   return render(request, 'mainapp/subcategory.html', context)
 
 

Widok obsługuje następujące rzeczy: wyświetlanie nazwy podkategorii, wyświetlanie podstron przypisanych do danej podkategorii, obsługę formularza skróconego (tylko adres URL). Po walidacji wprowadzonego adresu url, z widoku usuwana jest lista podstron i wyświetlany jest pełny formularz dodawania nowej strony do bazy. Czy obecna forma jest akceptowalna, czy powinno być to zrobione w inny sposób?
Zapisane
« Odpowiedz #1 : 14:31 09/01/17 »
sztosz Offline
Expert Python User

Zobacz profil WWW
****

Reputacja: 75
Płeć: Mężczyzna
Wiadomości: 584


A nie myślałeś o tym żeby logikę biznesową wyrzucić do jakiś nowych klas/obiektów odpowiadających stricte za logikę biznesową, a w kontrolerze tylko przypisisać do contentx efekt to co zwrócą wywołania metod tych nowych klas?
Zapisane
« Odpowiedz #2 : 18:13 09/01/17 »
jundymek Offline
Advanced Python User

Zobacz profil
**

Reputacja: 3
Wiadomości: 286


Pisząc logika biznesowa masz na myśli?:
Kod
keywords = form_extended.cleaned_data['keywords']
name = form_extended.cleaned_data['name']
description = form_extended.cleaned_data['description']
 

Myślałem, żeby wszystko co się da upchnąć do pliku .py, ale cały czas zastanawiam się jak to zrobić żeby to miało ręce i nogi...
Zapisane
« Odpowiedz #3 : 18:18 09/01/17 »
damosc Offline
Hello World!

Zobacz profil
*

Reputacja: 9
Wiadomości: 51


Ja bym przerzucił kod na CBV:
https://docs.djangoproject.com/en/1.10/topics/class-based-views/

oprócz oficjalnej dokumentacji pomaga też:
https://ccbv.co.uk
Zapisane
« Odpowiedz #4 : 21:53 09/01/17 »
sztosz Offline
Expert Python User

Zobacz profil WWW
****

Reputacja: 75
Płeć: Mężczyzna
Wiadomości: 584


Myślałem, żeby wszystko co się da upchnąć do pliku .py, ale cały czas zastanawiam się jak to zrobić żeby to miało ręce i nogi...
Ja tam staram się nie "upychać" kodu gdzie się da. Tylko komponować kod w niewielkie klasy które odpowiadają za tylko jedną niewielką rzecz, albo komponują się z takich właśnie małych klas, a w tych klasach staram się mieć małe metody których nazwy oddają jasno intencję kodu, tak, żeby było to czytelne już na pierwszy rzut oka i dla innych i dla mnie po jakimś czasie.
Zapisane
« Odpowiedz #5 : 22:46 09/01/17 »
jundymek Offline
Advanced Python User

Zobacz profil
**

Reputacja: 3
Wiadomości: 286


Też się staram tak robić, ale łatwo nie jest, szczególnie, że każdy problem na jaki napotykam to nauka nowej kwestii z kategorii python/django. Sam niestety często po czasie nie mogę się połapać we własnym kodzie i to jest zasadniczy problem.

Cytuj
Ja bym przerzucił kod na CBV:
Myślałem nad tym, ale CBV są trudniejsze na początek i trochę przeraża mnie teraz przerabianie wszystkiego, szczególnie, że trochę czasu już poświęciłem na napisanie działającego zarysu tego co sobie postawiłem za cel. Jestem w jakiejś 1/40 drogi do końca projektu...Mrugnięcie Może zrobię sobie gałąź gita i równolegle pobawię się CBV. A nóż widelec stwierdzę, że warto...
Zapisane
« Odpowiedz #6 : 10:05 10/01/17 »
sztosz Offline
Expert Python User

Zobacz profil WWW
****

Reputacja: 75
Płeć: Mężczyzna
Wiadomości: 584


Akurat mi się nigdy nie podobało tworzenie kontrolerów w postaci funkcji, zawsze wolałem CBV, bo wydawało mi się to jakieś takie logiczniejsze. Albo róbmy wszystko w sposób funkcyjny, albo obiektowy. Takie mieszanie tu klasa, tam funkcja wcale ani nie ułatwia niczego ani też nie utrudnia. Ale CBV daje nam ładny rozdział GET i POST bez konieczności if-owania w funkcji, po prostu sprawia że kod jest czytelniejszy, bo niby dlaczego jedna funkcja miałaby obsługiwać dwa zupełnie inne żądania jakimi są GET i POST? A co z PUT i PATCH, albo DELETE też byś to do tej samej funkcji dołożył? Mrugnięcie
Zapisane
« Odpowiedz #7 : 01:37 11/01/17 »
jundymek Offline
Advanced Python User

Zobacz profil
**

Reputacja: 3
Wiadomości: 286


Kurcze - siedzę od godziny nad CBV i niestety kompletnie nie mogę zrobić jednego prostego widoku. Chciałem dla próby zrobić widok subkategorii, który wyświetli mi jedną subkategorię i wylistuje strony przypisane do tej subkategorii (tak jak dotychczasowy widok "subcategory" oparty na funkcji:
W indexie mam coś takiego:
Kategoria1
    Sub1
    Sub2

Wchodzę w Sub1 i ma się pokazać ten właśnie widok.
Zrobiłem sobie coś takiego:
Kod
class SubcategoryList(ListView):
   model = SubCategory
 
   def get_context_data(self, **kwargs):
       context = super(SubcategoryList, self).get_context_data(**kwargs)
       context['site_list'] = Site.objects.all()
       return context
 
,
ale nie mam pojęcia jak wczytać wyłącznie jedną subkategorię (dajmy na to sub1).
W urls.py mam coś takiego:
Kod
url(r'^(?P<category_name_slug>[\w\-]+)/(?P<subcategory_name_slug>[\w\-]+)/$',
       SubcategoryList.as_view()),
 

Czyli ścieżka wygląda tak: http://127.0.0.1:8000/zdrowie-i-uroda/fitness/
Chciałem zrobić widok dla zawartości fitness. Czy w tym przypadku powinno się użyć ListView czy DetailView? Jak wczytać konkretną subkategorię? Będę wdzięczny za jakiekolwiek wskazówki.
Zapisane
« Odpowiedz #8 : 02:34 11/01/17 »
sztosz Offline
Expert Python User

Zobacz profil WWW
****

Reputacja: 75
Płeć: Mężczyzna
Wiadomości: 584


A coś takiego nie działa:
Kod
class SiteList(ListView):
   model = Site
 
   def get_queryset(self):
       return Site.objects.filter(
           subcategory__category__slug=self.kwargs['category_name_slug'],
           subcategory__slug=self.kwargs['subcategory_name_slug']
       ).select_related()

I potem w template site_lists.html
Kod
...
   {% for site in object_list %}
     <p>{{ site.name }}</p>
   {% empty %}
     <p>No sites yet.</p>
   {% endfor %}
...
 

Strzelam oczywiście z tym get_queryset(), bo nie wiem jak dokładnie twoje modele wyglądają.

I jedna ważna sprawa
Cytuj
Chciałem dla próby zrobić widok subkategorii, który wyświetli mi jedną subkategorię i wylistuje strony przypisane do tej subkategorii
Mi bardziej wygląda to nie na widok subkategorii, ale widok listy stron należących do subkategorii. Bo widok subkategorii to byłby raczej DetailView który mógłby wyglądać tak:
Kod
class SubcategoryDetailView(DetailView):
 
   model = Subcategory
 
   def get_queryset(self):
        return super().select_related('sites')
 
I w widoku:
Kod
...
   <h1>{{ object.name}}</h1>
   {% for site in object.sites %}
     <p>{{ site.name }}</p>
   {% empty %}
     <p>No sites yet.</p>
   {% endfor %}
...
 
A wtedy regex w urlpatterns tak
Kod
url(r'^(?P<category_name_slug>[\w\-]+)/(?P<slug>[\w\-]+)/$',
       SubcategoryDetailView.as_view()),
Bo wydaje mi się że pole slug w subcategory nazywa się slug a nie subcategory_name_slug (na podstawie kodu w twoim pierwszym poście.)
Oczywiście znów strzelam. Poza tym od 9 miesiecy nie pisałem nic w django i mogłem coś gdzieś delikatnie sknocić po drodze Mrugnięcie
Zapisane
« Odpowiedz #9 : 00:38 12/01/17 »
jundymek Offline
Advanced Python User

Zobacz profil
**

Reputacja: 3
Wiadomości: 286


Na razie udało mi się zrobić w klasach dwa podstawowe widoki:  stronę główną wyświetlającą listę katetegoii/podkategorii oraz  stronę szczegółów kategorii. Problem mam z najtrudniejszym czyli stroną subkategorii, gdzie wyświetlane są wszystkie strony z danej podkategorii i na dole jest formularz do dodania nowej strony. W widoku funkcyjnym jakoś udało mi się to rozplanować, żeby formularz A po wypełnieniu (adres www) został sprawdzony p/k poprawności oraz występowania w bazie i wartość z tego formularza została pobrana do formularza B, który zawiera wszystkie pola (wszystko w obrębie jednego widoku). W CBV nie mam pojęcia jak sobie z tym poradzić.

Dwa widoki podstawowe okazały się banalnie proste. W zasadzie trzeba było tylko utworzyć klasę opartą o widoki generyczne i wczytać dane bezpośrednio w templatce. Wyszło sporo mniej kodu niż w widokach funkcyjnych.

Kod
class CategoryList(ListView):
   model = Category
 
 
class CategoryDetailView(DetailView):
   model = Category
   template_name = 'mainapp/category.html'
 

Ma ktoś pomysł jaki widok powinien zostać użyty do sytuacji z podkategoriami (lista stron i na dole formularz z jednym polem, który po wypełnieniu wymaże listę stron i "rozwinie" formularz do pełnego rozmiaru - pola url, description, keywords itd. ? Nie może to być CreateView - tyle wydedukowałem po błędach (formularz A - ten skócony, nie zapisuje nic do bazy).
Zapisane
« Odpowiedz #10 : 12:06 12/01/17 »
sztosz Offline
Expert Python User

Zobacz profil WWW
****

Reputacja: 75
Płeć: Mężczyzna
Wiadomości: 584


Formularz "pełnego wymiaru" jako FormView.

A widok z podkategorią i listą stron, jako DetailView albo ListView (co wolisz, pisałem w poprzednim poście) + w samej templatce ręcznie formularz, z tym że, żeby nie komplikować tego FormView pełnego wymiaru, wysyłał bym dane GET'em do niego.
Zapisane
« Odpowiedz #11 : 01:12 13/01/17 »
jundymek Offline
Advanced Python User

Zobacz profil
**

Reputacja: 3
Wiadomości: 286


Muszę po raz kolejny prosić o jakąś podpowiedź. Zrobiłem sobie dwa widoki:

1. Formularz rozszerzony:

Kod
class AddSiteView(FormView):
   template_name = 'mainapp/add_site.html'
   form_class = SiteAddFormFull
   success_url = '/thanks/'
 
   def form_valid(self, form):
       pass
 

2. Podstrona ze stronami dla określonej podkategorii, gdzie znajduje się "okrojony" formularz do dodania strony

Kod
class SubcategoryListView(ListView):
   model = Site
   template_name = 'mainapp/subcat_class.html'
 
   def get_context_data(self, **kwargs):
       context = super(SubcategoryListView, self).get_context_data(**kwargs)
       context['object_list'] = Site.objects.filter(subcategory__slug=self.kwargs['slug'])
       context['subcategory'] = SubCategory.objects.get(slug=self.kwargs['slug'])
       return context
 

Tutaj mam urle dla tych widoków, ale nie wiem jak je poprawnie skonstruować:
Kod
    url(r'^(?P<category_name_slug>[\w\-]+)/(?P<slug>[\w\-]+)/$',
       SubcategoryListView.as_view()),
   url(r'^(?P<category_name_slug>[\w\-]+)/(?P<slug>[\w\-]+)/(?P<url>[\w\-]+)$',
       AddSiteView.as_view()),
 


Poniżej formularz znajdujący się na dole widoku SubcategoryListView:
Kod
        <form action="" method="get">
           <label for="url">Podaj url: </label>
           <input type="url" name="url" required="true">
       </form>
 

W jaki sposób przejść z jednego widoku do drugiego pobierając getem wprowadzony url? Przepraszam za pewnie dla niektórych banalne pytania, ale próbuję to wszystko jakoś ogarnąć i idzie mi to jak krew z nosa. Niby czasami posuwam się naprzód, ale za chwilę zderzam się ze ścianą i nie mam pojęcia jak ruszyć do przodu...
Zapisane
« Odpowiedz #12 : 02:18 13/01/17 »
sztosz Offline
Expert Python User

Zobacz profil WWW
****

Reputacja: 75
Płeć: Mężczyzna
Wiadomości: 584


Nie jestem pewien czy dobrze Cię rozumiem, ale chodzi o to że w formularzu dla SubcategoryListView nie podałeś w action na jaki url ma przejść.

Pierwsza rzecz to nazwać urle
Kod
url(r'^(?P<category_name_slug>[\w\-]+)/(?P<slug>[\w\-]+)/(?P<url>[\w\-]+)$',
       AddSiteView.as_view(), name='czary-mary'),
 

Zamiast:
Kod
 <form action="" method="get">
zrób
Kod
 <form action="{% url 'czary-mary' category.slug %}" method="get">
I wydaje mi się że url z automatu się doda ładnie do tego url pod który ma iść wprost z formularza... ale głowy nie dam, a nie mam żadnej przykładowej apki pod ręką żeby sprawdzić :/
Zapisane
« Odpowiedz #13 : 13:13 13/01/17 »
jundymek Offline
Advanced Python User

Zobacz profil
**

Reputacja: 3
Wiadomości: 286


Ok. Udało mi się zrobić przejście do pełnego formularza Uśmiech:

urls.py
Kod
url(r'^(?P<category_name_slug>[\w\-]+)/(?P<slug>[\w\-]+)/add/$',
       AddSiteView.as_view(), name='add-site'),
 

subcat_class.html
Kod
        <form action="{% url 'add-site' subcategory.category.slug subcategory.slug%}"
           method="get">
           <label for="url">Podaj url: </label>
           <input type="url" name="url" required="true">
       </form>
 

Dane są pobierane z formularza1 i automatycznie wczytywany jest widok z pełnym formularzem. Muszę jednak dokonać validacji wprowadzonych danych w pierwszym formularzu. Czy przy zastosowanej metodzie jest to w ogóle możliwe? Muszę sprawdzić czy podany url się wczytuje oraz czy istnieje już taki url w bazie. Dopiero po sprawdzeniu strona powinna przejść do formularza pełnego. Validacja powinna się odbyć przed przejściem do widoku formularza pełnego czy już w nowym widoku?
Zapisane
« Odpowiedz #14 : 11:37 14/01/17 »
raydeal Offline
Professional Python User

Zobacz profil
***

Reputacja: 55
Wiadomości: 316


Cytuj
Validacja powinna się odbyć przed przejściem do widoku formularza pełnego czy już w nowym widoku?
Walidacja oczywiście że przed przejściem, w widoku który w przypadku niepowodzenia walidacji zwróci widok poprzedni a w przypadku sukcesu widok pełnego formularza. Możesz to zrobić w tym samym widoku co formularz przed przejściem i ewentualnie zrobić redirect jeśli pełny formularz jest widoczny pod innym urlem.
Zapisane
Strony: [1] 2   Do góry
Drukuj
Skocz do:  

© 2007 - 2017 Polish Python Coders Group
Powered by SMF 1.1.21 | SMF © 2006-2009, Simple Machines | Theme by PixelSlot