Merge branch 'Kerl/postgres' into 'master'

Préparation au passage à postgres
- Suppression du champ num du modèle CofProfile.
- Suppression des LOCK (spécifique MySQL).
- Le code devient compatible avec tous les backends supportés par Django.
- Suppressions de code servant à la compatibilité python2.
- Corrige le message de succès à la fin de l'inscription. Celui-ci ne prenait pas en compte le statut is_cof à jour du profil.

See merge request !234
This commit is contained in:
Aurélien Delobelle 2017-05-23 22:50:10 +02:00
commit 0815c96c1c
8 changed files with 133 additions and 149 deletions

View file

@ -1,9 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from django import forms from django import forms
from django.contrib import admin from django.contrib import admin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -18,13 +12,12 @@ from django.contrib.auth.admin import UserAdmin
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.db.models import Q from django.db.models import Q
import django.utils.six as six
import autocomplete_light import autocomplete_light
def add_link_field(target_model='', field='', link_text=six.text_type, def add_link_field(target_model='', field='', link_text=str,
desc_text=six.text_type): desc_text=str):
def add_link(cls): def add_link(cls):
reverse_name = target_model or cls.model.__name__.lower() reverse_name = target_model or cls.model.__name__.lower()
@ -139,7 +132,6 @@ def ProfileInfo(field, short_description, boolean=False):
User.profile_login_clipper = FkeyLookup("profile__login_clipper", User.profile_login_clipper = FkeyLookup("profile__login_clipper",
"Login clipper") "Login clipper")
User.profile_num = FkeyLookup("profile__num", "Numéro")
User.profile_phone = ProfileInfo("phone", "Téléphone") User.profile_phone = ProfileInfo("phone", "Téléphone")
User.profile_occupation = ProfileInfo("occupation", "Occupation") User.profile_occupation = ProfileInfo("occupation", "Occupation")
User.profile_departement = ProfileInfo("departement", "Departement") User.profile_departement = ProfileInfo("departement", "Departement")
@ -166,10 +158,12 @@ class UserProfileAdmin(UserAdmin):
is_cof.short_description = 'Membre du COF' is_cof.short_description = 'Membre du COF'
is_cof.boolean = True is_cof.boolean = True
list_display = ('profile_num',) + UserAdmin.list_display \ list_display = (
UserAdmin.list_display
+ ('profile_login_clipper', 'profile_phone', 'profile_occupation', + ('profile_login_clipper', 'profile_phone', 'profile_occupation',
'profile_mailing_cof', 'profile_mailing_bda', 'profile_mailing_cof', 'profile_mailing_bda',
'profile_mailing_bda_revente', 'is_cof', 'is_buro', ) 'profile_mailing_bda_revente', 'is_cof', 'is_buro', )
)
list_display_links = ('username', 'email', 'first_name', 'last_name') list_display_links = ('username', 'email', 'first_name', 'last_name')
list_filter = UserAdmin.list_filter \ list_filter = UserAdmin.list_filter \
+ ('profile__is_cof', 'profile__is_buro', 'profile__mailing_cof', + ('profile__is_cof', 'profile__is_buro', 'profile__mailing_cof',
@ -215,21 +209,17 @@ class UserProfileAdmin(UserAdmin):
# FIXME: This is absolutely horrible. # FIXME: This is absolutely horrible.
def user_unicode(self): def user_str(self):
if self.first_name and self.last_name: if self.first_name and self.last_name:
return "%s %s (%s)" % (self.first_name, self.last_name, self.username) return "{} ({})".format(self.get_full_name(), self.username)
else: else:
return self.username return self.username
if six.PY2: User.__str__ = user_str
User.__unicode__ = user_unicode
else:
User.__str__ = user_unicode
class EventRegistrationAdmin(admin.ModelAdmin): class EventRegistrationAdmin(admin.ModelAdmin):
form = autocomplete_light.modelform_factory(EventRegistration, exclude=[]) form = autocomplete_light.modelform_factory(EventRegistration, exclude=[])
list_display = ('__unicode__' if six.PY2 else '__str__', 'event', 'user', list_display = ('__str__', 'event', 'user', 'paid')
'paid')
list_filter = ('paid',) list_filter = ('paid',)
search_fields = ('user__username', 'user__first_name', 'user__last_name', search_fields = ('user__username', 'user__first_name', 'user__last_name',
'user__email', 'event__title') 'user__email', 'event__title')

View file

@ -1,21 +1,13 @@
# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.forms.widgets import RadioSelect, CheckboxSelectMultiple from django.forms.widgets import RadioSelect, CheckboxSelectMultiple
from django.forms.formsets import BaseFormSet, formset_factory from django.forms.formsets import BaseFormSet, formset_factory
from django.db.models import Max
from django.core.validators import MinLengthValidator from django.core.validators import MinLengthValidator
from gestioncof.models import CofProfile, EventCommentValue, \ from gestioncof.models import CofProfile, EventCommentValue, \
CalendarSubscription, Club CalendarSubscription, Club
from gestioncof.widgets import TriStateCheckbox from gestioncof.widgets import TriStateCheckbox
from gestioncof.shared import lock_table, unlock_table
from bda.models import Spectacle from bda.models import Spectacle
@ -243,7 +235,6 @@ class RegistrationProfileForm(forms.ModelForm):
self.fields['mailing_cof'].initial = True self.fields['mailing_cof'].initial = True
self.fields['mailing_bda'].initial = True self.fields['mailing_bda'].initial = True
self.fields['mailing_bda_revente'].initial = True self.fields['mailing_bda_revente'].initial = True
self.fields['num'].widget.attrs['readonly'] = True
self.fields.keyOrder = [ self.fields.keyOrder = [
'login_clipper', 'login_clipper',
@ -251,7 +242,6 @@ class RegistrationProfileForm(forms.ModelForm):
'occupation', 'occupation',
'departement', 'departement',
'is_cof', 'is_cof',
'num',
'type_cotiz', 'type_cotiz',
'mailing_cof', 'mailing_cof',
'mailing_bda', 'mailing_bda',
@ -259,24 +249,9 @@ class RegistrationProfileForm(forms.ModelForm):
'comments' 'comments'
] ]
def save(self, *args, **kw):
instance = super(RegistrationProfileForm, self).save(*args, **kw)
if instance.is_cof and not instance.num:
# Generate new number
try:
lock_table(CofProfile)
aggregate = CofProfile.objects.aggregate(Max('num'))
instance.num = aggregate['num__max'] + 1
instance.save()
self.cleaned_data['num'] = instance.num
self.data['num'] = instance.num
finally:
unlock_table(CofProfile)
return instance
class Meta: class Meta:
model = CofProfile model = CofProfile
fields = ("login_clipper", "num", "phone", "occupation", fields = ("login_clipper", "phone", "occupation",
"departement", "is_cof", "type_cotiz", "mailing_cof", "departement", "is_cof", "type_cotiz", "mailing_cof",
"mailing_bda", "mailing_bda_revente", "comments") "mailing_bda", "mailing_bda_revente", "comments")

View file

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('gestioncof', '0010_delete_custommail'),
]
operations = [
migrations.RemoveField(
model_name='cofprofile',
name='num',
),
]

View file

@ -1,11 +1,7 @@
# -*- coding: utf-8 -*-
from django.db import models from django.db import models
from django.dispatch import receiver from django.dispatch import receiver
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import python_2_unicode_compatible
import django.utils.six as six
from django.db.models.signals import post_save, post_delete from django.db.models.signals import post_save, post_delete
from gestioncof.petits_cours_models import choices_length from gestioncof.petits_cours_models import choices_length
@ -35,12 +31,10 @@ TYPE_COMMENT_FIELD = (
) )
@python_2_unicode_compatible
class CofProfile(models.Model): class CofProfile(models.Model):
user = models.OneToOneField(User, related_name="profile") user = models.OneToOneField(User, related_name="profile")
login_clipper = models.CharField("Login clipper", max_length=8, blank=True) login_clipper = models.CharField("Login clipper", max_length=8, blank=True)
is_cof = models.BooleanField("Membre du COF", default=False) is_cof = models.BooleanField("Membre du COF", default=False)
num = models.IntegerField("Numéro d'adhérent", blank=True, default=0)
phone = models.CharField("Téléphone", max_length=20, blank=True) phone = models.CharField("Téléphone", max_length=20, blank=True)
occupation = models.CharField(_("Occupation"), occupation = models.CharField(_("Occupation"),
default="1A", default="1A",
@ -72,7 +66,7 @@ class CofProfile(models.Model):
verbose_name_plural = "Profils COF" verbose_name_plural = "Profils COF"
def __str__(self): def __str__(self):
return six.text_type(self.user.username) return self.user.username
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
@ -86,7 +80,6 @@ def post_delete_user(sender, instance, *args, **kwargs):
instance.user.delete() instance.user.delete()
@python_2_unicode_compatible
class Club(models.Model): class Club(models.Model):
name = models.CharField("Nom", max_length=200, unique=True) name = models.CharField("Nom", max_length=200, unique=True)
description = models.TextField("Description", blank=True) description = models.TextField("Description", blank=True)
@ -98,7 +91,6 @@ class Club(models.Model):
return self.name return self.name
@python_2_unicode_compatible
class Event(models.Model): class Event(models.Model):
title = models.CharField("Titre", max_length=200) title = models.CharField("Titre", max_length=200)
location = models.CharField("Lieu", max_length=200) location = models.CharField("Lieu", max_length=200)
@ -115,10 +107,9 @@ class Event(models.Model):
verbose_name = "Événement" verbose_name = "Événement"
def __str__(self): def __str__(self):
return six.text_type(self.title) return self.title
@python_2_unicode_compatible
class EventCommentField(models.Model): class EventCommentField(models.Model):
event = models.ForeignKey(Event, related_name="commentfields") event = models.ForeignKey(Event, related_name="commentfields")
name = models.CharField("Champ", max_length=200) name = models.CharField("Champ", max_length=200)
@ -130,10 +121,9 @@ class EventCommentField(models.Model):
verbose_name = "Champ" verbose_name = "Champ"
def __str__(self): def __str__(self):
return six.text_type(self.name) return self.name
@python_2_unicode_compatible
class EventCommentValue(models.Model): class EventCommentValue(models.Model):
commentfield = models.ForeignKey(EventCommentField, related_name="values") commentfield = models.ForeignKey(EventCommentField, related_name="values")
registration = models.ForeignKey("EventRegistration", registration = models.ForeignKey("EventRegistration",
@ -144,7 +134,6 @@ class EventCommentValue(models.Model):
return "Commentaire de %s" % self.commentfield return "Commentaire de %s" % self.commentfield
@python_2_unicode_compatible
class EventOption(models.Model): class EventOption(models.Model):
event = models.ForeignKey(Event, related_name="options") event = models.ForeignKey(Event, related_name="options")
name = models.CharField("Option", max_length=200) name = models.CharField("Option", max_length=200)
@ -154,10 +143,9 @@ class EventOption(models.Model):
verbose_name = "Option" verbose_name = "Option"
def __str__(self): def __str__(self):
return six.text_type(self.name) return self.name
@python_2_unicode_compatible
class EventOptionChoice(models.Model): class EventOptionChoice(models.Model):
event_option = models.ForeignKey(EventOption, related_name="choices") event_option = models.ForeignKey(EventOption, related_name="choices")
value = models.CharField("Valeur", max_length=200) value = models.CharField("Valeur", max_length=200)
@ -167,10 +155,9 @@ class EventOptionChoice(models.Model):
verbose_name_plural = "Choix" verbose_name_plural = "Choix"
def __str__(self): def __str__(self):
return six.text_type(self.value) return self.value
@python_2_unicode_compatible
class EventRegistration(models.Model): class EventRegistration(models.Model):
user = models.ForeignKey(User) user = models.ForeignKey(User)
event = models.ForeignKey(Event) event = models.ForeignKey(Event)
@ -184,11 +171,9 @@ class EventRegistration(models.Model):
unique_together = ("user", "event") unique_together = ("user", "event")
def __str__(self): def __str__(self):
return "Inscription de %s à %s" % (six.text_type(self.user), return "Inscription de {} à {}".format(self.user, self.event.title)
six.text_type(self.event.title))
@python_2_unicode_compatible
class Survey(models.Model): class Survey(models.Model):
title = models.CharField("Titre", max_length=200) title = models.CharField("Titre", max_length=200)
details = models.TextField("Détails", blank=True) details = models.TextField("Détails", blank=True)
@ -199,10 +184,9 @@ class Survey(models.Model):
verbose_name = "Sondage" verbose_name = "Sondage"
def __str__(self): def __str__(self):
return six.text_type(self.title) return self.title
@python_2_unicode_compatible
class SurveyQuestion(models.Model): class SurveyQuestion(models.Model):
survey = models.ForeignKey(Survey, related_name="questions") survey = models.ForeignKey(Survey, related_name="questions")
question = models.CharField("Question", max_length=200) question = models.CharField("Question", max_length=200)
@ -212,10 +196,9 @@ class SurveyQuestion(models.Model):
verbose_name = "Question" verbose_name = "Question"
def __str__(self): def __str__(self):
return six.text_type(self.question) return self.question
@python_2_unicode_compatible
class SurveyQuestionAnswer(models.Model): class SurveyQuestionAnswer(models.Model):
survey_question = models.ForeignKey(SurveyQuestion, related_name="answers") survey_question = models.ForeignKey(SurveyQuestion, related_name="answers")
answer = models.CharField("Réponse", max_length=200) answer = models.CharField("Réponse", max_length=200)
@ -224,10 +207,9 @@ class SurveyQuestionAnswer(models.Model):
verbose_name = "Réponse" verbose_name = "Réponse"
def __str__(self): def __str__(self):
return six.text_type(self.answer) return self.answer
@python_2_unicode_compatible
class SurveyAnswer(models.Model): class SurveyAnswer(models.Model):
user = models.ForeignKey(User) user = models.ForeignKey(User)
survey = models.ForeignKey(Survey) survey = models.ForeignKey(Survey)
@ -244,7 +226,6 @@ class SurveyAnswer(models.Model):
self.survey.title) self.survey.title)
@python_2_unicode_compatible
class CalendarSubscription(models.Model): class CalendarSubscription(models.Model):
token = models.UUIDField() token = models.UUIDField()
user = models.OneToOneField(User) user = models.OneToOneField(User)

View file

@ -12,15 +12,15 @@ from django.views.decorators.csrf import csrf_exempt
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib import messages from django.contrib import messages
from django.db import transaction
from gestioncof.models import CofProfile from gestioncof.models import CofProfile
from gestioncof.petits_cours_models import ( from gestioncof.petits_cours_models import (
PetitCoursDemande, PetitCoursAttribution, PetitCoursAttributionCounter, PetitCoursDemande, PetitCoursAttribution, PetitCoursAttributionCounter,
PetitCoursAbility, PetitCoursSubject PetitCoursAbility
) )
from gestioncof.petits_cours_forms import DemandeForm, MatieresFormSet from gestioncof.petits_cours_forms import DemandeForm, MatieresFormSet
from gestioncof.decorators import buro_required from gestioncof.decorators import buro_required
from gestioncof.shared import lock_table, unlock_tables
class DemandeListView(ListView): class DemandeListView(ListView):
@ -274,17 +274,17 @@ def _traitement_post(request, demande):
headers={'Reply-To': replyto})) headers={'Reply-To': replyto}))
connection = mail.get_connection(fail_silently=False) connection = mail.get_connection(fail_silently=False)
connection.send_messages(mails_to_send) connection.send_messages(mails_to_send)
lock_table(PetitCoursAttributionCounter, PetitCoursAttribution, User) with transaction.atomic():
for matiere in proposals: for matiere in proposals:
for rank, user in enumerate(proposals[matiere]): for rank, user in enumerate(proposals[matiere]):
counter = PetitCoursAttributionCounter.objects.get(user=user, counter = PetitCoursAttributionCounter.objects.get(
matiere=matiere) user=user, matiere=matiere
)
counter.count += 1 counter.count += 1
counter.save() counter.save()
attrib = PetitCoursAttribution(user=user, matiere=matiere, attrib = PetitCoursAttribution(user=user, matiere=matiere,
demande=demande, rank=rank + 1) demande=demande, rank=rank + 1)
attrib.save() attrib.save()
unlock_tables()
demande.traitee = True demande.traitee = True
demande.traitee_par = request.user demande.traitee_par = request.user
demande.processed = datetime.now() demande.processed = datetime.now()
@ -309,8 +309,7 @@ def inscription(request):
profile.petits_cours_accept = "receive_proposals" in request.POST profile.petits_cours_accept = "receive_proposals" in request.POST
profile.petits_cours_remarques = request.POST["remarques"] profile.petits_cours_remarques = request.POST["remarques"]
profile.save() profile.save()
lock_table(PetitCoursAttributionCounter, PetitCoursAbility, User, with transaction.atomic():
PetitCoursSubject)
abilities = ( abilities = (
PetitCoursAbility.objects.filter(user=request.user).all() PetitCoursAbility.objects.filter(user=request.user).all()
) )
@ -319,7 +318,6 @@ def inscription(request):
ability.user, ability.user,
ability.matiere ability.matiere
) )
unlock_tables()
success = True success = True
formset = MatieresFormSet(instance=request.user) formset = MatieresFormSet(instance=request.user)
else: else:

View file

@ -1,15 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.conf import settings from django.conf import settings
from django_cas_ng.backends import CASBackend from django_cas_ng.backends import CASBackend
from django_cas_ng.utils import get_cas_client from django_cas_ng.utils import get_cas_client
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import connection
from gestioncof.models import CofProfile from gestioncof.models import CofProfile
@ -74,25 +67,3 @@ def context_processor(request):
"site": Site.objects.get_current(), "site": Site.objects.get_current(),
} }
return data return data
def lock_table(*models):
query = "LOCK TABLES "
for i, model in enumerate(models):
table = model._meta.db_table
if i > 0:
query += ", "
query += "%s WRITE" % table
cursor = connection.cursor()
cursor.execute(query)
row = cursor.fetchone()
return row
def unlock_tables(*models):
cursor = connection.cursor()
cursor.execute("UNLOCK TABLES")
row = cursor.fetchone()
return row
unlock_table = unlock_tables

View file

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
import unicodecsv import unicodecsv
import uuid import uuid
from datetime import timedelta from datetime import timedelta
@ -14,7 +12,6 @@ from django.contrib.auth.models import User
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.utils import timezone from django.utils import timezone
from django.contrib import messages from django.contrib import messages
import django.utils.six as six
from gestioncof.models import Survey, SurveyAnswer, SurveyQuestion, \ from gestioncof.models import Survey, SurveyAnswer, SurveyQuestion, \
SurveyQuestionAnswer SurveyQuestionAnswer
@ -403,12 +400,8 @@ def registration_form2(request, login_clipper=None, username=None,
def registration(request): def registration(request):
if request.POST: if request.POST:
request_dict = request.POST.copy() request_dict = request.POST.copy()
# num ne peut pas être défini manuellement
if "num" in request_dict:
del request_dict["num"]
member = None member = None
login_clipper = None login_clipper = None
success = False
# ----- # -----
# Remplissage des formulaires # Remplissage des formulaires
@ -445,7 +438,6 @@ def registration(request):
member = user_form.save() member = user_form.save()
profile, _ = CofProfile.objects.get_or_create(user=member) profile, _ = CofProfile.objects.get_or_create(user=member)
was_cof = profile.is_cof was_cof = profile.is_cof
request_dict["num"] = profile.num
# Maintenant on remplit le formulaire de profil # Maintenant on remplit le formulaire de profil
profile_form = RegistrationProfileForm(request_dict, profile_form = RegistrationProfileForm(request_dict,
instance=profile) instance=profile)
@ -499,15 +491,17 @@ def registration(request):
for club in clubs_form.cleaned_data['clubs']: for club in clubs_form.cleaned_data['clubs']:
club.membres.add(member) club.membres.add(member)
club.save() club.save()
success = True
# Messages # ---
if success: # Success
# ---
msg = ("L'inscription de {:s} (<tt>{:s}</tt>) a été " msg = ("L'inscription de {:s} (<tt>{:s}</tt>) a été "
"enregistrée avec succès" "enregistrée avec succès."
.format(member.get_full_name(), member.email)) .format(member.get_full_name(), member.email))
if member.profile.is_cof: if profile.is_cof:
msg += "Il est désormais membre du COF n°{:d} !".format( msg += "\nIl est désormais membre du COF n°{:d} !".format(
member.profile.num) member.profile.id)
messages.success(request, msg, extra_tags='safe') messages.success(request, msg, extra_tags='safe')
return render(request, "gestioncof/registration_post.html", return render(request, "gestioncof/registration_post.html",
{"user_form": user_form, {"user_form": user_form,
@ -572,10 +566,10 @@ def export_members(request):
writer = unicodecsv.writer(response) writer = unicodecsv.writer(response)
for profile in CofProfile.objects.filter(is_cof=True).all(): for profile in CofProfile.objects.filter(is_cof=True).all():
user = profile.user user = profile.user
bits = [profile.num, user.username, user.first_name, user.last_name, bits = [profile.id, user.username, user.first_name, user.last_name,
user.email, profile.phone, profile.occupation, user.email, profile.phone, profile.occupation,
profile.departement, profile.type_cotiz] profile.departement, profile.type_cotiz]
writer.writerow([six.text_type(bit) for bit in bits]) writer.writerow([str(bit) for bit in bits])
return response return response
@ -591,10 +585,10 @@ def csv_export_mega(filename, qs):
comments = "---".join( comments = "---".join(
[comment.content for comment in reg.comments.all()]) [comment.content for comment in reg.comments.all()])
bits = [user.username, user.first_name, user.last_name, user.email, bits = [user.username, user.first_name, user.last_name, user.email,
profile.phone, profile.num, profile.phone, profile.id,
profile.comments if profile.comments else "", comments] profile.comments if profile.comments else "", comments]
writer.writerow([six.text_type(bit) for bit in bits]) writer.writerow([str(bit) for bit in bits])
return response return response
@ -613,8 +607,8 @@ def export_mega_remarksonly(request):
user = reg.user user = reg.user
profile = user.profile profile = user.profile
bits = [user.username, user.first_name, user.last_name, user.email, bits = [user.username, user.first_name, user.last_name, user.email,
profile.phone, profile.num, profile.comments, val.content] profile.phone, profile.id, profile.comments, val.content]
writer.writerow([six.text_type(bit) for bit in bits]) writer.writerow([str(bit) for bit in bits])
return response return response

57
kfet/tests/test_views.py Normal file
View file

@ -0,0 +1,57 @@
from decimal import Decimal
from django.test import TestCase, Client
from django.contrib.auth.models import User
from django.utils import timezone
from ..models import Account, OperationGroup, Checkout, Operation
class AccountTests(TestCase):
"""Account related views"""
def setUp(self):
# A user and its account
self.user = User.objects.create_user(username="foobar", password="foo")
acc = Account.objects.create(
trigramme="FOO", cofprofile=self.user.profile
)
# Dummy operations and operation groups
checkout = Checkout.objects.create(
created_by=acc, name="checkout",
valid_from=timezone.now(),
valid_to=timezone.now() + timezone.timedelta(days=365)
)
opeg_data = [
(timezone.now(), Decimal('10')),
(timezone.now() - timezone.timedelta(days=3), Decimal('3')),
]
OperationGroup.objects.bulk_create([
OperationGroup(
on_acc=acc, checkout=checkout, at=at, is_cof=False,
amount=amount
)
for (at, amount) in opeg_data
])
self.operation_groups = OperationGroup.objects.order_by("-amount")
Operation.objects.create(
group=self.operation_groups[0],
type=Operation.PURCHASE,
amount=Decimal('10')
)
Operation.objects.create(
group=self.operation_groups[1],
type=Operation.PURCHASE,
amount=Decimal('3')
)
def test_account_read(self):
"""We can query the "Accout - Read" page."""
client = Client()
self.assertTrue(client.login(
username="foobar",
password="foo"
))
resp = client.get("/k-fet/accounts/FOO")
self.assertEqual(200, resp.status_code)