Bonjour à tous,
Voila j'ai un projet d'une Webapp de ticket (support informatique) avec django 1.8 et python 3.3 : https://github.com/hadmagic/Aidez-moi

J'ai un table tickets et j'aimerais pouvoir mettre d'en une autre table toutes les éditions du tickets. J'ai déjà essayé django-simple-history et django-reversion et cela ne me convient pas.
En fait j'aimerais pouvoir garder les attributs de chaque colonne du ticket dans l'historique (par exemple les choix du status etc...)
Pour le moment j'ai juste surcharger la méthode save() de TicketForm :

model.py :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
 
 
class UserProfile(models.Model):
 
    user = models.OneToOneField(User)
 
 
 
class Tickets( models.Model):
 
    title = models.TextField()
    content = models.TextField()
    create_by = models.ForeignKey(User)
    created = models.DateTimeField()
    last_edited = models.DateTimeField(auto_now=True)
 
    TYPES_CHOICES = (
        (1, 'Incident'),
        (2, 'Demande'),)
 
    types = models.IntegerField(
        ('Types'),
        choices=TYPES_CHOICES )
 
    OPEN_STATUS = 'OPEN'
    RESOLVED_STATUS = 3
    CLOSED_STATUS = 4
 
    STATUS_CHOICES = (
        ('OPEN', 'Open'),
        ('RESOLVED', 'Resolved'),
        ('CLOSED', 'Closed'),
    )
 
    PRIORITY_CHOICES = (
        ('CRITICAL', 'Critical'),
        ('HIGH', 'High'),
        ('NORMAL', 'Normal'),
        ('LOW', 'Low'),
        ('VERYLOW', 'Very Low'),)
 
    assign_to = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        related_name='assigned_to',
        blank=True,
        null=True,
        verbose_name=('Assigned to'),
        )
 
    status = models.CharField(max_length=15
        ,
        choices=STATUS_CHOICES,
        default=OPEN_STATUS, )
 
    priority = models.CharField(max_length=15
        ,
        choices=PRIORITY_CHOICES,
        default='NORMAL',
        blank='NORMAL',
        help_text=('1 = Highest Priority, 5 = Low Priority'),)
 
 
 
    def __str__(self):
        """
        Cette méthode que nous définirons dans tous les modèles
        nous permettra de reconnaître facilement les différents objets que
        nous traiterons plus tard et dans l'administration
        """
        return self.title
 
 
class Follow(models.Model):
 
    follow_by = models.ForeignKey(User, related_name='follower')
    follow = models.TextField(blank=True, null=True)
    ticket = models.ForeignKey(Tickets, related_name='ticket_id')
    date_follow = models.DateTimeField(auto_now=True, )
 
    field = models.CharField(max_length=100, null=True)
    old_value = models.TextField(null=True)
    new_value = models.TextField(null=True)
forms.py :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
__author__ = 'had'
 
from django import forms
from ticket.models import User, Tickets, UserProfile, Follow
from model_utils import FieldTracker
 
 
class ConnexionForm(forms.Form):
    """
    Pour la page de login
    """
    username = forms.CharField(label="Nom d'utilisateur", max_length=30,
                               widget=forms.TextInput(attrs={
                                                            'type':"text",
                                                            'placeholder':"Username"}))
 
    password = forms.CharField(label="Mot de passe",
                               widget=forms.PasswordInput(attrs={
                                                            'type':"password",
                                                            'placeholder':"Password"}))
 
 
class TicketForm(forms.ModelForm):
    """
    Pour ajouter un ticket
    """
 
    title = forms.CharField(label='Titre',widget=forms.TextInput(attrs={'placeholder': 'Titre',
                                                                        'size':'110',
                                                                        }))
 
    content = forms.CharField(label='Ticket',widget=forms.Textarea(attrs={'placeholder': 'Contenu du ticket',
                                                                          'rows':'5',
 
                                                                          'class':'uk-width-1-1'}))
 
    priority = forms.ChoiceField(
        choices=Tickets.PRIORITY_CHOICES,
        required=True,
        initial='3',
        label=('Urgency'),
        help_text=('Please select a priority carefully.'),
         )
 
    # Pour choisir que les membres du staff
    assign_to = forms.ModelChoiceField(queryset=User.objects.all().filter(is_staff=1))
 
    class Meta:
        model = Tickets
        exclude = ('created', 'create_by')
 
    def __init__(self, *args, **kwargs):
        """
        Pour exclure certains champs de la classe TicketForm
        afin d'afficher assign_to et status pour un membre du staff
        """
        user = kwargs.pop('user', None)
        super(TicketForm, self).__init__(*args, **kwargs)
        if user.is_staff is False:
            del self.fields['assign_to']
            del self.fields['status']
 
    def edit(self,ticket_id, user, *args, **kwargs):
        print(args)
        if  Tickets.objects.filter(id=ticket_id).exists():
            if self.has_changed():
                ticket = Tickets.objects.filter(pk=ticket_id)
                for field in self.changed_data:
                    oldvalue = ticket.values(field)
                    Follow.objects.create(
                                    ticket_id=ticket_id,
                                    field=field,
                                    old_value=oldvalue[0].get(field),
                                    new_value=self[field].value(),
                                    follow_by=user
                                     )
        else:
            pass
 
        super(TicketForm, self).save(*args, **kwargs)
 
class ResponseForm(forms.ModelForm):
    follow = forms.CharField(label='Ticket',widget=forms.Textarea(
        attrs={'placeholder': 'Réponse au ticket','rows':'4','class':'uk-width-1-1'}))
 
    class Meta:
        model = Follow
        fields = ['follow']
        exclude = ('date_follow', 'ticket_id', 'field', 'new_value', 'old_value', 'follower')
views.py :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
__author__ = 'had'
 
from django.shortcuts import render, redirect, render_to_response
from ticket.forms import TicketForm, ResponseForm
from ticket.models import Tickets, UserProfile, Follow
from ticket.views.auth import home
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import render_to_response, get_object_or_404
from django.template import loader, Context, RequestContext
from django.contrib import messages
from datetime import datetime
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.views.decorators.cache import cache_page
from django.core.cache import cache
from datetime import datetime
from itertools import chain
 
 
PER_PAGE = 100
 
@cache_page(60*1)
@login_required(login_url='login/')
def add_ticket(request):
    if request.method == 'POST':
        form = TicketForm(data=request.POST, user=request.user)
        # return redirect('/')
        if form.is_valid():
            ticket = form.save(commit=False)
            ticket.create_by = request.user
            ticket.created = datetime.now()
            ticket.save()
            return redirect(home)
        else:
            return render(request, 'add_ticket.html', locals())
    else:
        form = TicketForm(user=request.user)
    return render(request, 'add_ticket.html', locals())
 
 
#@cache_page(60*1)
@login_required(login_url='login/')
def ticket_list_new(request):
    if request.user.is_staff:
        ticket_list = Tickets.objects.filter(assign_to=None).order_by('-created')
    else:
        ticket_list = Tickets.objects.filter(create_by=request.user, assign_to=None).order_by('-created')
    paginator = Paginator(ticket_list, PER_PAGE)
 
    page = request.GET.get('page')
    try:
        tickets = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        tickets = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        tickets = paginator.page(paginator.num_pages)
 
    return render(request, 'ticket_list.html', locals())
 
#@cache_page(60*1)
@login_required(login_url='login/')
def ticket_list_work(request):
 
    if request.user.is_staff:
        ticket_list = Tickets.objects.select_related('create_by', 'assign_to').prefetch_related('create_by')\
            .filter(status='OPEN').exclude(assign_to=None).order_by('-created')
    else:
        ticket_list = Tickets.objects.select_related('create_by', 'assign_to').prefetch_related('create_by')\
            .filter(create_by=request.user, status='OPEN').exclude(assign_to=None).order_by('-created')
 
    paginator = Paginator(ticket_list, PER_PAGE)
 
    page = request.GET.get('page')
    try:
        tickets = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        tickets = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        tickets = paginator.page(paginator.num_pages)
    # return render_to_response('index.html', {"articles": articles})
 
    #return render_to_response('ticket_list.html',
        #RequestContext(request, {
            #'ticket': ticket,}))
    return render(request, 'ticket_list.html', locals())
 
 
#@cache_page(60*1)
@login_required(login_url='login/')
def ticket_list_resolved(request):
    if request.user.is_staff:
        ticket_list = Tickets.objects.select_related('create_by', 'assign_to')\
            .filter(status='RESOLVED').exclude(assign_to=None).order_by('-created')
    else:
        ticket_list = Tickets.objects.select_related('create_by', 'assign_to').prefetch_related('create_by')\
            .filter(create_by=request.user, status='RESOLVED').order_by('-created')
 
    paginator = Paginator(ticket_list, PER_PAGE)
 
    page = request.GET.get('page')
    try:
        tickets = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        tickets = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        tickets = paginator.page(paginator.num_pages)
    # return render_to_response('index.html', {"articles": articles})
    return render(request, 'ticket_list.html', locals())
 
#@cache_page(60*1)
@login_required(login_url='login/')
def ticket_list_clos(request):
    if request.user.is_staff:
        ticket_list = Tickets.objects.select_related('create_by', 'assign_to').prefetch_related('create_by', 'assign_to')\
            .filter(status='CLOSED').exclude(assign_to=None).order_by('-created')
    else:
        ticket_list = Tickets.objects.select_related('create_by', 'assign_to').prefetch_related('create_by')\
            .filter(create_by=request.user, status='CLOSED').order_by('-created')
 
    paginator = Paginator(ticket_list, PER_PAGE)
 
    page = request.GET.get('page')
    try:
        tickets = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        tickets = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        tickets = paginator.page(paginator.num_pages)
    # return render_to_response('index.html', {"articles": articles})
    return render(request, 'ticket_list.html', locals())
 
 
 
 
#@cache_page(60*1)
@login_required(login_url='login/')
def ticket_all(request):
    ticket_list = Tickets.objects.select_related('create_by', 'assign_to')
    paginator = Paginator(ticket_list, PER_PAGE)
 
    page = request.GET.get('page')
    try:
        tickets = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        tickets = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        tickets = paginator.page(paginator.num_pages)
    # return render_to_response('index.html', {"articles": articles})
    return render(request, 'ticket_list.html', locals())
 
 
#@cache_page(60*15)
@login_required(login_url='login/')
def ticket_edit(request, id):
    ticket = get_object_or_404(Tickets, id=id)
    if request.method=='POST' and 'edit' in request.POST:
        form = TicketForm(request.POST, user=request.user, instance=ticket)
 
        if form.is_valid():
            #form.edit(commit=False, ticket_id=id, user=request.user)
            form.save()
            #messages.add_message(request, messages.INFO, 'Ticket mis à jour OK')
            return redirect(view_ticket, id)
            # If the save was successful, redirect to another page
    else:
            form = TicketForm(user=request.user, instance=ticket)
            response = ResponseForm()
 
    return render(request, 'add_ticket.html', locals())
 
 
 
 
 
@login_required(login_url='login/')
def view_ticket(request, id):
 
    tickets = Tickets.objects.select_related('create_by').get(id=id)
    follow_up = Follow.objects.select_related('follow_by', 'ticket').filter(ticket=id)
 
    if request.method == 'POST':
        form = ResponseForm(data=request.POST)
        #if form.is_valid():
        follow = form.save(commit=False)
        follow.ticket_id=id
        follow.follow_by=request.user
        follow.save()
    else:
        form = ResponseForm()
 
    return render(request,'ticket.html', locals())
Merci à tous