„Django Sklep - Konta dla nowych użytkowników”
W tym rozdziale przedstawimy, jak w prosty sposób stworzyć bazę użytkowników oraz system rejestracji i logowania. Dzięki temu każdy użytkownik będzie miał swój własny koszyk. Zatem do dzieła. Zobacz, jakie to łatwe. Ten rozdział będzie nieco dłuższy, ale spokojnie, nawet nie zauważysz, kiedy skończysz tworzyć bazę danych.
Najpierw, aby zachować porządek w naszych plikach i umożliwić implementację bazy użytkowników, stworzymy nową aplikację w projekcie. Gdy masz już aktywny projekt (w ramach przypomnienia) w otwartym terminalu, w katalogu, w którym znajdują się wszystkie pliki:
Projekt1_env\Scripts\activate
Gdy projekt jest aktywny (pojawiły się nawiasy), wpisz:
python manage.py startapp baza
Zobacz, że teraz w strukturze projektu pojawił się folder „baza” z takimi plikami, jak folder „Sklep”.
Teraz w pliku Settings.py dopisz nazwę nowej aplikacji:
'baza',
Teraz zaktualizuj plik urls.py:
from baza.views import *
from django.urls import path, include
oraz dodaj Django informację, że stworzymy nowe linki dla aplikacji „baza”:
path('baza/', include('baza.urls')),
To bardziej uporządkowany sposób na łączenie linków z funkcjami w Django. Przy większej ilości aplikacji w jednym projekcie zalecamy właśnie takie podejście.
Czyli w pliku głównym dajemy znać Django: „Hej, tworzymy nowe ścieżki, które zaczynają się od „baza” i będą znajdowały się w pliku „urls” w folderze „baza”.
Skorzystamy z wbudowanej funkcji na bazę danych, którą oferuje Django. Aby to zrobić, w naszym szablonie zdefiniuj nowe linki do rejestracji, logowania i wylogowania.
Dodajmy również, aby wyświetlała się nazwa użytkownika, jeśli jest zalogowany. Zmodyfikuj plik szablon.html w następujący sposób:
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Sklep</title>
</head>
<body>
<h1>Witaj w naszym sklepie</h1><br>
<p>
<a href="/">Strona Główna</a> <a href="/produkty">Pokaż mi swoje towary</a>
<a href="/koszyk">Koszyk</a>
{% if user.is_authenticated %}
Witaj, {{user.username}}.
<a href="{% url 'baza:logout' %}">Wyloguj</a>
{% else %}
<a href="{% url 'baza:register' %}">Rejestracja</a>
<a href="{% url 'baza:login' %}">Zaloguj</a>
{% endif %}
</p>
{% block wszystko %}{% endblock wszystko %}
</body>
</html>
Dodaliśmy trzy, póki co puste, linki, kryjące się pod np. napisem „Wyloguj”, z akceptowalną formą kodu.
Teraz w aplikacji „baza” stwórz plik urls.py, a następnie przepisz poniższy kod:
from django.urls import path, include
from . import views
app_name = 'baza'
urlpatterns = [
path('', include('django.contrib.auth.urls')),
path('register/', views.register, name ='register'),
]
Funkcje „login” i „logout” są już zdefiniowane w podstawowych funkcjach Django, dlatego nie musimy ich tutaj zapisywać, ale musimy zachować odpowiednią formę, żeby z nich skorzystać.
Jak zauważyłeś na zdjęciu powyżej, pojawiły się również trzy nowe pliki HTML: login, register oraz logged_out, które znajdują się w strukturze baza->templates->registration i wyglądają następująco:
{% extends 'Sklep/szablon.html' %}
{% block wszystko %}
{% if form.errors %}
Spróbuj ponownie, coś pomieszałeś.
{% endif %}
{% endblock wszystko %}
{% extends 'Sklep/szablon.html' %}
{% block wszystko %}
Wylogowałeś się, wszystko git.
{% endblock wszystko %}
{% extends 'Sklep/szablon.html' %}
{% block wszystko %}
{% endblock wszystko %}
Wszystkie trzy pliki dziedziczą strukturę po szablonie, ale aby można było wykorzystać gotową bazę danych z Django, trzeba było dostosować kod do standardów przedstawionych powyżej.
W pliku views.py w aplikacji „baza” dodaj jedną funkcję:
from django.shortcuts import render, redirect
from django.contrib.auth import login
from django.contrib.auth.forms import UserCreationForm
def register(request):
if request.method != 'POST':
form = UserCreationForm()
else:
form = UserCreationForm(data=request.POST)
if form.is_valid():
new_user = form.save()
login(request, new_user)
return redirect('/')
context = {'form': form}
return render(request, 'registration/register.html', context)
Sprawdź, czy wszystko działa, spróbuj się zalogować na konto utworzone na samym początku administratora, potem dodaj kilka kont na testy (po prostu zarejestruj kilka nowych kont). Powinno to wyglądać mniej więcej tak:
Gotowa rejestracja prezentuje się jak poniżej. Na tym etapie taka wystarczy:
Oraz widok bazy danych z poziomu administratora, pokazujący całą listę wszystkich zarejestrowanych użytkowników z kodowanymi hasłami:
PS: Klasa „Koszyk” będzie omówiona w dalszej kolejności.
Wchodząc głębiej, możesz zobaczyć, że jest kilka dodatkowych funkcji, z których można skorzystać. Jednak na razie pozostaniemy przy standardowej wersji.
Teraz, gdy mamy już kilka utworzonych kont, zmodyfikujemy naszą aplikację tak, aby koszyk był prywatny dla każdego konta i wysyłał spersonalizowane zamówienie. DO IT!
Można to zrealizować na wiele sposobów. My dodamy nową klasę, która będzie powiązana z utworzoną wcześniej bazą danych poprzez atrybut „nazwa użytkownika” oraz powiązana z wszystkimi produktami. W ten sposób, po dodaniu konkretnego produktu do koszyka, zostanie utworzona kopia obiektu klasy Produkt (z atrybutami: nazwa, cena, dodane) np. „Kamerki”, ale dodatkowo z nazwą danego użytkownika. Docelowo w bazie danych Django będzie to wyglądało mniej więcej tak:
Aplikacji „baza” już nie będziemy modyfikować, będziemy z niej tylko korzystać. Wracamy do aplikacji „Sklep”.
Najpierw stworzymy nową klasę „Koszyk”, która będzie miała te same atrybuty co klasa „Produkt”, ale dodatkowo będzie zależna od zalogowanego użytkownika. Dodaj import „User” oraz nową klasę w pliku models.py w aplikacji „Sklep”:
from django.contrib.auth.models import User
class Koszyk(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
nazwa_user = models.CharField(max_length=50)
opis_user = models.TextField(blank=True)
cena_user = models.TextField(blank=True)
dodane_user = models.IntegerField(default=0)
def str(self):
name = str(self.nazwa_user) + " - " + str(self.user)
return name
class Meta:
verbose_name = "Koszyk"
verbose_name_plural = "Koszyk"
W pliku admin.py dodaj klasę „Koszyk”:
from django.contrib import admin
# Register your models here.
from .models import Produkt, Koszyk
admin.site.register(Produkt)
admin.site.register(Koszyk)
W pliku settings.py dopisz frazę na samym dole:
LOGIN_URL = 'baza:login'
Wykonaj standardową procedurę po modyfikacji pliku models.py – w konsoli wpisz dwie komendy po sobie:
python manage.py makemigrations
python manage.py migrate
Teraz zajmiemy się modyfikacją pliku views.py w aplikacji „Sklep”. Chcemy, aby wszystkie funkcje powiązane z koszykiem oddziaływały na obiekty z klasy „Koszyk”, a nie klasy „Produkt”. Po pierwsze, dodaj odpowiednie biblioteki:
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
# Create your views here.
from Sklep.models import Produkt, Koszyk
from .forms import KoszykForm
Fraza @login_required oznacza, że funkcja wykona się dopiero, gdy użytkownik jest zalogowany. W przeciwnym razie nastąpi przekierowanie do strony logowania.
Dodaj funkcję „dodajkoszyk”:
@login_required
def dodajkoszyk(request, id):
produkt = Produkt.objects.get(pk=id)
p_id = id
if Koszyk.objects.filter(user=request.user, nazwa_user=produkt.nazwa).exists():
produkt_koszyk = Koszyk.objects.get(user=request.user, nazwa_user=produkt.nazwa)
else:
Koszyk.objects.create(user=request.user, nazwa_user=produkt.nazwa, cena_user=produkt.cena, dodane_user=produkt.dodane)
produkt_koszyk = Koszyk.objects.get(user=request.user, nazwa_user=produkt.nazwa)
if request.method != 'POST':
form = KoszykForm(instance=produkt_koszyk)
else:
form = KoszykForm(instance=produkt_koszyk, data=request.POST)
if form.is_valid():
form.save()
dane = {'produkt': produkt, 'p_id': p_id, 'form': form}
return render(request, 'Sklep/produkt.html', dane)
Wprowadziliśmy warunek na aktualizację ilości danego produktu, jeśli ten obiekt został już utworzony, lub jego utworzenie dziedzicząc atrybuty po danym produkcie i jednocześnie zalogowanym użytkowniku. Teraz należy zaktualizować formularz, aby odnosił się do klasy „Koszyk”:
from django import forms
from .models import Produkt, Koszyk
class KoszykForm(forms.ModelForm):
class Meta:
model = Produkt
fields = ['dodane']
labels = {'dodane': ''}
class KoszykForm(forms.ModelForm):
class Meta:
model = Koszyk
fields = ['dodane_user']
labels = {'dodane_user': ''}
Pliku „produkt.html” nie musimy modyfikować. Jego wygląd będzie bazował na klasie „Produkty”, natomiast pod powłoką komputer będzie czarował i dodawał obiekty typu „Koszyk”. Wracamy do pliku i modyfikujemy następną funkcję „koszyk”:
@login_required
def koszyk(request):
produkty_koszyk = Koszyk.objects.filter(user=request.user)
cena = 0
for produkt in produkty_koszyk:
cena = cena + float(produkt.cena_user) * int(produkt.dodane_user)
cena = float(round(cena, 2))
dane = {'produkty_koszyk': produkty_koszyk, 'cena': cena}
return render(request, 'Sklep/koszyk.html', dane)
Ta funkcja wyśle do pliku „koszyk.html” produkty w koszyku konkretnego zalogowanego użytkownika. Zaktualizujemy teraz plik „koszyk.html”:
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Sklep</title>
</head>
<body>
<h1>Twój koszyk:</h1>
<p>
<a href="/">Strona Główna</a>
<a href="/produkty">Pokaż mi swoje towary</a>
<a href="/koszyk">Koszyk</a>
{% if user.is_authenticated %}
Witaj, {{user.username}}.
<a href="{% url 'baza:logout' %}">Wyloguj</a>
{% else %}
<a href="{% url 'baza:register' %}">Rejestracja</a>
<a href="{% url 'baza:login' %}">Zaloguj</a>
{% endif %}
</p>
{% block wszystko %}
<p>Twój koszyk:</p>
{% if produkty_koszyk %}
{% for produkt in produkty_koszyk %}
<p>nazwa: {{produkt.nazwa_user}}, ilość zakupionych sztuk: {{produkt.dodane_user}}
<a href="/kosz/1/{{produkt.id}}">+</a>
<a href="/kosz/0/{{produkt.id}}">-</a>
</p>
{% endfor %}
{% else %}
<p>Twój koszyk jest pusty jak słoik po ogórkach</p>
{% endif %}
<br>
<p>Całkowita kwota do zapłaty: {{cena}} Złotych</p>
<a href="/wyslij">Złóż zamówienie</a>
{% endblock wszystko %}
</body>
</html>
@login_required
def kosz(request, id1, id2):
produkt_wyb = Koszyk.objects.get(pk=id2)
if id1 == '1':
produkt_m = int(produkt_wyb.dodane_user) + 1
else:
produkt_m = int(produkt_wyb.dodane_user) - 1
Koszyk.objects.filter(pk=id2).update(dodane_user=produkt_m)
produkty_koszyk = Koszyk.objects.filter(user=request.user)
cena = 0
for produkt in produkty_koszyk:
cena = cena + float(produkt.cena_user) * int(produkt.dodane_user)
cena = float(round(cena, 2))
dane = {'produkty_koszyk': produkty_koszyk, 'cena': cena}
return render(request, 'Sklep/koszyk.html', dane)
W końcu dodajemy funkcję „wyslij”:
@login_required
def wyslij(request):
produkty_koszyk = Koszyk.objects.filter(user=request.user)
cena = 0
for produkt in produkty_koszyk:
cena = cena + float(produkt.cena_user) * int(produkt.dodane_user)
cena = float(round(cena, 2))
file = open(f'C:/Users/Marcin/Desktop/zamowienie_{request.user}.txt', "w")
file.write(f"rachunek użytkownika: {request.user}\n")
for obiekt in produkty_koszyk:
file.write(f"nazwa: {obiekt.nazwa_user}, ilość zakupionych sztuk: {obiekt.dodane_user}\n")
file.write(f"kwota do zapłaty: {cena}")
file.close()
Koszyk.objects.filter(user=request.user).delete()
produkty_koszyk = False
cena = 0
dane = {'produkty_koszyk': produkty_koszyk, 'cena': cena}
return render(request, 'Sklep/koszyk.html', dane)
Koniec! Udało się stworzyć bardzo funkcjonalną stronę z produktami, którą można rozszerzać na wiele sposobów. Teraz zaloguj się na kilka kont i sprawdź, czy wszystko działa. W rezultacie powinna pojawić się inna lista produktów w koszyku dla każdego użytkownika.