Pomoc w poprawieniu łatwiutkiej aplikacji webowej

0

Cześć! Pracę z pythonem dopiero zaczynam, chcę napisać prostą aplikację webową.
Moim pomysłem jest "aplikacja", na której:

  • rzucamy kostką
  • wyświetla się łączna suma oczek wyrzuconych przez gościa
  • wyświetlają się ostatnie rzuty
    Oczywiście jest to bardzo prymitywne, jednak chciałbym posłuchać rad i jak do tego wszystkiego dojść, poniżej wrzucam kod programu (z błędami, nie mogę dojść do tego jak zrobić wyświetlenie ostatnich rzutów żeby sie to trzymało kupy)

PS. Pracę wykonuje na repl.it

from flask import Flask, url_for, request
import random

def create_app ():
    app = Flask(__name__)
    rzuty = {}
    ostatnie = []

    @app.route('/', defaults={'name': 'Guest'})
    @app.route('/user/<name>')
    def index (name):
      Suma_rzuconych_oczek = rzuty.get(name, 0)
      return f'''
        <h2>{name} rzuca kostka</h2>
        <p>Lacznie {name} rzucil {Suma_rzuconych_oczek} :-)</p>
        <ul><li><a href="{url_for('add', name=name, nowy=10)}">Rzuc kostka</a></li>
        <li><a href="{url_for('users', back=name)}">Zobacz wszystkich rzucajacych i ich wyniki</a></li>
        <li><a href="{url_for('lastest', back=name)}">Zobacz ostatnie rzuty</a></li></ul>
      '''

    @app.route('/add/<name>', defaults={'nowy': 10})
    @app.route('/add/<name>/<int:nowy>')
    def add (name, nowy):
      nowy = random.randint(1, 6)
      rzuty[name] = rzuty.get(name, 0) + nowy
      ostatnie.append(nowy)
      return f'''
        <h1>Oczka dodane</h1>
        <p>{name} wyrzucil {nowy} oczek.</p>
        <a href="{url_for('index', name=name)}">Back to rzuty</a>
      '''
    @app.route('/users/')
    def users ():
      points = [f'''
                <li><a href="{url_for('index', name=name)}">{name}</a>: {rz}</li>
                ''' for name, rz in rzuty.items()]
      name = request.args.get('back')
      if name:
        back_url = url_for('index', name=name)
      else:
        back_url = url_for('index')
      return f'''
        <h1>Wszyscy rzucający</h1>
        <ul>{''.join(points)}</ul>
        <a href="{back_url}">Back to {name}'s stats</a>
      '''
    
    @app.route('/lastest/')
    def lastest ():
      points = [f'''
                <li><a href="{url_for('index', name=name)}">{name}</a>: [ostatnie]</li>
                ''' for name, nowy in ostatnie.items()]
      name = request.args.get('back')
      if name:
        back_url = url_for('index', name=name)
      else:
        back_url = url_for('index')
      return f'''
        <h1>Ostatnie rzuty</h1>
        <ul>{''.join(points)}</ul>
        <a href="{back_url}">Back to {name}'s stats</a>
      '''
    
    return app

create_app().run('0.0.0.0')

Jeżeli również jest to jakoś w miare łatwę, myślałem aby było cos w stylu wybierania iloma kostkami rzucamy

0

Na pewno nie pomaga zapis/odczyt wyników ze zmiennej (sic) będącej zmienną lokalną funkcji (sic x2) w aplikacji webowej która potencjalnie może (i przeważnie tak jest) działać na wielu wątkach / procesach, przez co może dochodzić (i pewnie dochodzi) do

  • wyścigów przy dostępie do zmiennej (np. jednoczesna modyfikacja przez dwa wątki, odczyt przez jeden gdy drugi zapisuje etc)
  • sytuacji, gdzie tworzony jest proces do obsugi żądania HTTP, proces dostaje własny kawałek pamięci, w nim zapisuje sobie rzut kostką, zwraca odpowiedź, proces jest zamykany, pamięć leci do recyklingu

Poczytaj sobie o SQLAlchemy, bazach danych, redisie, a o zapisywaniu czegokolwiek w zmiennych zapomnij, zaoraj, posyp to solą.

Poza tym zwracanie zahardkodowanych kawałków HTML jest... słabe. Od tego masz zasoby (taki specjalny folder resources) żeby dodawać tam chociażby te pliki HTML, żeby był jakiś porządek. A jak chcesz np. zwrocić HTMLa z listą tych wyrzuconych kostek czy czymś, to czytasz o szablonach Jinja2 i wykorzystujesz szablony i render_template by wygenerować w locie.

0

Zatem do wyświetlania ostatnich rzutów będzie mi potrzebna baza danych?

  • ten ostatni app.route do wyrzucenia?

No cóż, zmieniłem to na to

from flask import Flask, url_for, request
import random


def create_app ():
    app = Flask(__name__)
    rzuty = {}

    @app.route('/', defaults={'name': 'Guest'})
    @app.route('/user/<name>')
    def index (name):
      Suma_rzuconych_oczek = rzuty.get(name, 0)
      return f'''
        <h2>{name} rzuca kostka</h2>
        <p>Lacznie {name} rzucil {Suma_rzuconych_oczek} :-)</p>
        <ul><li><a href="{url_for('add', name=name, nowy=10)}">Rzuc kostka</a></li>
        <li><a href="{url_for('users', back=name)}">Zobacz wszystkich rzucajacych i ich wyniki</a></li>
      '''

    @app.route('/add/<name>', defaults={'nowy': 10})
    @app.route('/add/<name>/<int:nowy>')
    def add (name, nowy):
      nowy = random.randint(1, 6)
      rzuty[name] = rzuty.get(name, 0) + nowy
      return f'''
        <h1>Oczka dodane</h1>
        <p>{name} wyrzucil {nowy} oczek.</p>
        <a href="{url_for('index', name=name)}">Back to rzuty</a>
      '''
    @app.route('/users/')
    def users ():
      points = [f'''
                <li><a href="{url_for('index', name=name)}">{name}</a>: {rz}</li>
                ''' for name, rz in rzuty.items()]
      name = request.args.get('back')
      if name:
        back_url = url_for('index', name=name)
      else:
        back_url = url_for('index')
      return f'''
        <h1>Wszyscy rzucający</h1>
        <ul>{''.join(points)}</ul>
        <a href="{back_url}">Back to {name}'s stats</a>
      '''
    
    return app

create_app().run('0.0.0.0') 

Obecnie nie potrzebuję zwracać tego inaczej niż widzę to na repl.it, czyli zwyczajnie na zwykłej stronie na przeglądarce i też średnio wiem o co chodzi, ponieważ programuję właśnie tam i nie miałem z tym styczności o czym mówisz. Chciałbym wiedzieć czy w takim razie do wyśietlania ostatnich rzutów będzie potrzebna baza danych? Z tego również nie korzystałem na replu. Zapewne przy wyborze iloma kostkami chcemy rzucać również baza danych?

1

To nie musi być koniecznie jakaś wypasiona baza danych z diabli wiedzą czym, do czegoś takiego wystarczy Ci w zupełności np. SQLite. Baza SQLite jest w pliku, nie ma dedykowanego serwera który nią jakoś zarządza więc nie ma dużo konfigurowania.

Tak uogólniając - potrzebujesz czegoś, co pozwala przechować stan (np. licznik rzutów) między obsługą jednego żądania, a obsługą drugiego, bo nie możesz oczekiwać że aplikacja sama go przechowa.

I jeszcze jedno - poczytaj o typach żądań HTTP. Robienie rzutu kostką zmienia stan (bo pojawia się nowy wynik) więc nie powinien to być GET, będący domyślną metodą dla app.route:

@app.route('/foo/bar', methods=['POST'])
def add_new_bar():
  # ....
1
adammo4321 napisał(a):

Obecnie nie potrzebuję zwracać tego inaczej niż widzę to na repl.it, czyli zwyczajnie na zwykłej stronie na przeglądarce i też średnio wiem o co chodzi, ponieważ programuję właśnie tam i nie miałem z tym styczności o czym mówisz. Chciałbym wiedzieć czy w takim razie do wyśietlania ostatnich rzutów będzie potrzebna baza danych? Z tego również nie korzystałem na replu. Zapewne przy wyborze iloma kostkami chcemy rzucać również baza danych?

Zainstaluj sobie interpreter Pythona (3.x, 2 odchodzi do lamusa), postaw wirtualne środowisko (venv), w nim już możesz sobie na luzie instalować wszelkie paczki itd. Wątpię, żeby na repl.it można było postawić sobie bazę danych do aplikacji, a lokalnie już jak najbardziej można.

Skąd Ci w ogóle przyszło do głowy uruchamiać serwer HTTP w jakimś zdalnym REPL?

0

Zainstaluj sobie interpreter Pythona (3.x, 2 odchodzi do lamusa), postaw wirtualne środowisko (venv), w nim już możesz sobie na luzie instalować wszelkie paczki itd. Wątpię, żeby na repl.it można było postawić sobie bazę danych do aplikacji, a lokalnie już jak najbardziej można.

Skąd Ci w ogóle przyszło do głowy uruchamiać serwer HTTP w jakimś zdalnym REPL?

Na studiach pracowaliśmy na repl.it, może w takim razie nie potrzebuje robić niczego z bazą danych i najprostsze rzeczy zostaną. Szukam co tu mogę dodać jeszcze łatwego.

0
adammo4321 napisał(a):

Na studiach pracowaliśmy na repl.it, może w takim razie nie potrzebuje robić niczego z bazą danych i najprostsze rzeczy zostaną. Szukam co tu mogę dodać jeszcze łatwego.

To jest projekt na zaliczenie? Jeśli tak i jeśli planujesz zostać przy tym, co już napisałeś i tylko "dodawać łatwe rzeczy" - to za napisanie aplikacji webowej trzymającej dane w zmiennych nie dałbym Ci nawet 2.0.

Jak koniecznie chcesz używać repl.it i przerasta Cię stawianie czegokolwiek lokalnie - jak już wspominałem SQLite nie wymaga stawiania osobnego serwera bazy.

0

Zajęcia z pythona mało miały wspólnego z nauką, ja lubowałem się stety lub nie w C++, dodatkowo tak jak mówię, może profesor coś umiał, jednak przekazać wiedzy we właściwy sposób już nie. Postaram się przejść z replita na zwykłego pythona i zrobić to z bazą danych, aby znajdowało się tam coś choć trochę ambitniejszego. Opornie idzie mi ten język i słabo mi idzie zrozumienie napisania aplikacji webowych + podchodzi do tego jeszcze dołączenie bazy i HTML, żeby to jakoś wyglądało. Ale postaram się w takim razie, widziałem w poradnikach katalogi "templates" czy pliki "index.html", ale nie rozumiałem jak to wszystko potworzyć i poustawiać odpowiednio.

1 użytkowników online, w tym zalogowanych: 0, gości: 1