djangofantalega: Asta
8 – Asta
Una fantalega che si rispetti inizia sempre con una Asta:
ogni giocatore (Player) viene associato ad una squadra (Team).
Nella template della Lega, è stato previsto un bottone
<input type="submit" class="btn" value="Start Auction" name="auction">
questo bottone va collegato alle urls, alle views e alle templates necessarie
allo scopo.
Aggiungere per prima cosa le urls relative all’asta (auction) nel file fantalega\urls.py:
... # auction urls url(r'^leagues/(?P<league_id>[0-9]+)/auction$', views.auction, name='auction'), url(r'^leagues/(?P<league_id>[0-9]+)/auction/summary$', views.auction_summary, name='auction_summary'), ]
Aggiungere nel file fantalega\views.py le nuove viste:
... @login_required def auction(request, league_id): league = get_object_or_404(League, pk=int(league_id)) if request.GET.get('back_to_teams'): return redirect('league_details', league.id) if request.GET.get('auction_summary'): return redirect('auction_summary', league.id) free_players = [(p.code, p.name) for p in Player.objects.filter(season=league.season).all() if not p.team] league_teams = [(team.id, team.name) for team in league.team_set.all()] if request.method == "POST": form = AuctionPlayer(request.POST, initial={'players': free_players, 'teams': league_teams, 'league': league}) if form.is_valid(): player_code = form.cleaned_data['player'] player = Player.get_by_code(int(player_code), season=league.season) auction_value = form.cleaned_data['auction_value'] team_id = form.cleaned_data['team'] team = Team.objects.get(pk=int(team_id)) if team.player_set.count() == league.max_players(): messages.warning(request, '%s has reached max player number' ' the operation is not valid' % team.name) return redirect('auction', league.id) remaining_players = league.max_players() - team.player_set.count() budget_remaining = int(team.budget) - int(auction_value) if budget_remaining >= 0 and budget_remaining >= \ (remaining_players - 1): player.team = team player.auction_value = auction_value player.save() team.budget -= auction_value team.save() messages.success(request, 'Auction operation [ %s - %s ] stored!' % (player.name, team.name)) else: messages.error(request, "Not enough budget: budget: %s, " "auction price %s, remaining players %s" % (team.budget, auction_value, remaining_players)) return redirect('auction', league.id) else: form = AuctionPlayer(initial={'players': free_players, 'teams': league_teams, 'league': league}) return render(request, 'fantalega/auction.html', {'form': form, 'players': free_players, 'teams': league_teams, 'league': league}, ) @login_required def auction_summary(request, league_id): league = get_object_or_404(League, pk=int(league_id)) league_teams = league.team_set.all() if request.GET.get('back_to_auction'): return redirect('auction', league.id) context = {'league': league, 'teams': league_teams} return render(request, 'fantalega/auction_summary.html', context)
il form che permetterà l’associazione Player-Team (fantalega/forms.py) sarà:
... class AuctionPlayer(forms.Form): def __init__(self, *args, **kwargs): self.dict_values = kwargs.pop('initial') super(AuctionPlayer, self).__init__(*args, **kwargs) self.fields['player'] = forms.ChoiceField( label=u'player', choices=self.dict_values['players'], widget=forms.Select(), required=False) self.fields['auction_value'] = forms.IntegerField() self.fields['team'] = forms.ChoiceField( label=u'team', choices=self.dict_values['teams'], widget=forms.Select(), required=False)
e andrà importato nel file fantalega/views.py:
from .forms import AuctionPlayer ...
La logica è semplice:
si chiama a turno un giocatore, si fa un’asta poi nel form, si immetteranno
il nome della squadra che si è aggiudicata il giocatore e il valore d’asta,
che verrà scalato dal budget totale di squadra.
Viene anche effettuato un controllo sul valore d’asta, per assicurarsi che
ogni squadra abbia la disponibilità sufficiente per rilanciare.
E’ prevista anche una view/template per il riassunto dell’asta (auction_summary).
Ora le template legate alle views suddette.
templates/auction.html
{% extends 'fantalega/base.html' %} {% load bootstrap3 %} {% block content %} <form action="#" method="get"> <input type="submit" class="btn" value="back to {{ league.name }} teams" name="back_to_teams"> <input type="submit" class="btn" value="view auction summary" name="auction_summary"> </form> <b>New Auction Operation for lega <font color="green">{{ league.name }}</font></b><br><br> <form method="POST" class="form"> {% csrf_token %} {% bootstrap_form form %} {% buttons %} <button type="submit" class="btn btn-primary"> {% bootstrap_icon "save" %} Submit</button> {% endbuttons %} </form> {% endblock %}
il pulsante che rimanderà al sommario è appunto:
<input type="submit" class="btn" value="view auction summary" name="auction_summary">
templates/auction_summary.html
{% extends 'fantalega/base.html' %} {% load bootstrap3 %} {% load app_filters %} {% block content %} <b><font color="green">{{ league.name }}</font>: <font color="purple">Auction summary</font></b><br> <br> <form action="#" method="get"> <input type="submit" class="btn" value="back to auction" name="back_to_auction"> </form> {% if league.team_set.all %} {% for team in league.team_set.all %} <font color="brown"><b>{{team.name}}</b></font> <font color="darkgrey"> {{team.user.username}}</font><br> {% for player in team.player_set.all %} {{player|color_by_role}}: <font color="darkgrey"> {{player.role}}</font><br> {% endfor %} <font color="red">budget remain: <b>{{team.budget}}</b><br></font> total goalkeepers to buy: {{team|gk_to_buy:league}}<br> total defenders to buy: {{team|def_to_buy:league}}<br> total players to buy: {{team|mid_to_buy:league}}<br> total players to buy: {{team|forw_to_buy:league}}<br> total players to buy: {{team|total_to_buy:league}} <br> <br> {% endfor %} {% else %} <p>No team found.</p> {% endif %} {% endblock %}
Collegare il pulsante ‘auction’ alla view ‘league_details’,
file fantalega.views.py:
@login_required def league_details(request, league_id): ... if request.GET.get('auction'): return redirect('auction', league.id) context = {'league': league, 'teams': league_teams, 'days': days, 'user': request.user} ...
Nella template auction_summary.html vengono utilizzati alcuni custom_filters,
che vanno inseriti nel file templatetags/app_filters.py
from django.utils.safestring import mark_safe ... @register.filter(name='color_by_role') def color_by_role(player): if player.role.lower() == 'goalkeeper': color = 'blue' elif player.role.lower() == 'defender': color = 'orange' elif player.role.lower() == 'midfielder': color = 'green' else: color = 'purple' new_string = '<font color="%s">%s</font>' % (color, player.name.lower()) return mark_safe(new_string) @register.filter(name='gk_to_buy') def gk_to_buy(team, league): gk_bought = team.player_set.filter(role='goalkeeper').count() remain = league.max_goalkeepers - gk_bought color = "green" if not remain else "red" return mark_safe('<b><font color="%s">%s</font></b>' % (color, remain)) @register.filter(name='def_to_buy') def def_to_buy(team, league): def_bought = team.player_set.filter(role='defender').count() remain = league.max_defenders - def_bought color = "green" if not remain else "red" return mark_safe('<b><font color="%s">%s</font></b>' % (color, remain)) @register.filter(name='mid_to_buy') def mid_to_buy(team, league): mid_bought = team.player_set.filter(role='midfielder').count() remain = league.max_midfielders - mid_bought color = "green" if not remain else "red" return mark_safe('<b><font color="%s">%s</font></b>' % (color, remain)) @register.filter(name='forw_to_buy') def forw_to_buy(team, league): forw_bought = team.player_set.filter(role='forward').count() remain = league.max_forwards - forw_bought color = "green" if not remain else "red" return mark_safe('<b><font color="%s">%s</font></b>' % (color, remain)) @register.filter(name='total_to_buy') def total_to_buy(team, league): player_bought = team.player_set.count() remain = league.max_players() - player_bought color = "green" if not remain else "red" return mark_safe('<b><font color="%s">%s</font></b>' % (color, remain))
Per comodità aggiungere il metodo max_players al model League,
metodo che torna utile durante l’asta per il controllo del tetto massimo
di giocatori.
fantalega/models.py
class League(models.Model): ... def max_players(self): return self.max_goalkeepers + self.max_defenders + \ self.max_midfielders + self.max_forwards
Avviare il server e recarsi nella pagina relativa alla lega principale,
premere sul pulsante ‘Start Auction’ ed effettuare un acquisto, ricordandosi
il valore d’asta.
Ad operazione ultimata recarsi alla pagina
‘view auction summary’ ed appariranno tutti i resoconti di ogni squadra.
E’ probabile che l’asta venga effettuata con l’ausilio di altri strumenti,
forniamo quindi il model League, di un metodo che permetta di importare
il risultato di un’asta direttamente da file csv:
fantalega/models.py
class League(models.Model): ... @staticmethod def auction_upload(path): """This method import Auction directly from a csv file with this format: 'name;auction_value;team_name 'team_name' is the name of the object Team """ with open(path) as data: for record in data: name, auction_value, team_name = record.strip().split(";") player = Player.objects.filter(name=name.upper()).first() if player: player.auction_value = int(auction_value) team = Team.objects.filter(name=team_name.strip()).first() if team: if player not in team.player_set.all(): player.team = team team.budget -= player.auction_value player.save() team.save() else: print "[ERROR] Team %s not found" % team_name else: print "[ERROR] Player %s not found" % name print "[INFO] Auction upload done!"
Ora è suffieciente aprire la shell
python manage.py shell >>> League.auction_upload('fantalega/static/teams.csv') >>> from fantalega.models import League ...
Salvare gli avanzamenti su github:
git add --all
git commit -m "Auction added"
git push -u origin master
La prossima sezione sarà piuttosto complicata, riguardando le formazioni (Lineup).
articoli precedenti
0 – indice
1 – Virtualenv e Git
2 – Models: Season
3 – Admin: Login e Logout
4 – Models: League
5 – Models: Team
6 – Models: Match
7 – Models: Player
articoli successivi
9 – Models: Lineup
10 – Models: Trade
11 – Asta di riparazione
12 – Classifica
Commenti recenti