mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-10-31 15:50:03 +01:00 
			
		
		
		
	interface pour ajouter des membres à un club
This commit is contained in:
		| @@ -6,7 +6,15 @@ from django.contrib.auth.forms import UserChangeForm, UserCreationForm | ||||
| from django.contrib.auth.models import User | ||||
| from django import forms | ||||
|  | ||||
| from .models import Profile, Club | ||||
| from .models import Profile, Club, Membership | ||||
|  | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
|  | ||||
| from crispy_forms.helper import FormHelper | ||||
| from crispy_forms import layout, bootstrap | ||||
| from crispy_forms.bootstrap import InlineField, FormActions, StrictButton, Div, Field | ||||
| from crispy_forms.layout import Layout | ||||
|  | ||||
|  | ||||
| class ProfileForm(forms.ModelForm): | ||||
|     """ | ||||
| @@ -21,3 +29,33 @@ class ClubForm(forms.ModelForm): | ||||
|     class Meta: | ||||
|         model = Club | ||||
|         fields ='__all__' | ||||
|  | ||||
| class AddMembersForm(forms.Form): | ||||
|     class Meta: | ||||
|         fields = ('',) | ||||
|  | ||||
| class MembershipForm(forms.ModelForm): | ||||
|     class Meta: | ||||
|         model = Membership | ||||
|         fields = ('user','roles','date_start') | ||||
|  | ||||
| MemberFormSet = forms.modelformset_factory(Membership, | ||||
|                                            form=MembershipForm, | ||||
|                                            extra=2, | ||||
|                                            can_delete=True) | ||||
|  | ||||
| class FormSetHelper(FormHelper): | ||||
|     def __init__(self,*args,**kwargs): | ||||
|         super().__init__(*args,**kwargs) | ||||
|         self.form_tag = False | ||||
|         self.form_method = 'POST' | ||||
|         self.form_class='form-inline' | ||||
|         # self.template = 'bootstrap/table_inline_formset.html' | ||||
|         self.layout = Layout( | ||||
|             Div( | ||||
|                 Div('user',css_class='col-sm-2'), | ||||
|                 Div('roles',css_class='col-sm-2'), | ||||
|                 Div('date_start',css_class='col-sm-2'), | ||||
|                 css_class="row formset-row", | ||||
|             ) | ||||
|         ) | ||||
|   | ||||
| @@ -13,6 +13,7 @@ urlpatterns = [ | ||||
|     path('signup/',views.UserCreateView.as_view(),name="signup"), | ||||
|     path('club/',views.ClubListView.as_view(),name="club_list"), | ||||
|     path('club/<int:pk>/',views.ClubDetailView.as_view(),name="club_detail"), | ||||
|     path('club/<int:pk>/add_member/',views.ClubAddMemberView.as_view(),name="club_add_member"), | ||||
|     path('club/create/',views.ClubCreateView.as_view(),name="club_create"), | ||||
|     path('user/<int:pk>',views.UserDetailView.as_view(),name="user_detail") | ||||
| ] | ||||
|   | ||||
| @@ -10,8 +10,8 @@ from django.http import HttpResponseRedirect | ||||
| from django.contrib.auth.forms import UserCreationForm | ||||
| from django.urls import reverse_lazy | ||||
|  | ||||
| from .models import Profile, Club | ||||
| from .forms import ProfileForm, ClubForm | ||||
| from .models import Profile, Club, Membership | ||||
| from .forms import ProfileForm, ClubForm,MembershipForm, MemberFormSet,FormSetHelper | ||||
|  | ||||
| class UserCreateView(CreateView): | ||||
|     """ | ||||
| @@ -24,7 +24,7 @@ class UserCreateView(CreateView): | ||||
|     second_form = UserCreationForm | ||||
|  | ||||
|     def get_context_data(self,**kwargs): | ||||
|         context = super(SignUp,self).get_context_data(**kwargs) | ||||
|         context = super().get_context_data(**kwargs) | ||||
|         context["user_form"] = self.second_form | ||||
|  | ||||
|         return context | ||||
| @@ -62,6 +62,25 @@ class ClubListView(LoginRequiredMixin,ListView): | ||||
|     form_class = ClubForm | ||||
|  | ||||
| class ClubDetailView(LoginRequiredMixin,DetailView): | ||||
|     """ | ||||
|     """ | ||||
|     model = Club | ||||
|  | ||||
| class ClubAddMemberView(LoginRequiredMixin,CreateView): | ||||
|     model = Membership | ||||
|     form_class = MembershipForm | ||||
|     template_name = 'member/add_members.html' | ||||
|     def get_context_data(self,**kwargs): | ||||
|         context = super().get_context_data(**kwargs) | ||||
|         context['formset'] = MemberFormSet() | ||||
|         context['helper'] = FormSetHelper() | ||||
|         return context | ||||
|     | ||||
|     def post(self,request,*args,**kwargs): | ||||
|         formset = MembershipFormset(request.POST) | ||||
|         if formset.is_valid(): | ||||
|             return self.form_valid(formset) | ||||
|         else: | ||||
|             return self.form_invalid(formset) | ||||
|  | ||||
|     def form_valid(self,formset): | ||||
|         formset.save() | ||||
|         return super().form_valid(formset) | ||||
|   | ||||
							
								
								
									
										231
									
								
								static/js/dynamic-formset.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								static/js/dynamic-formset.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,231 @@ | ||||
| /** | ||||
|  * jQuery Formset 1.3-pre | ||||
|  * @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com) | ||||
|  * @requires jQuery 1.2.6 or later | ||||
|  * | ||||
|  * Copyright (c) 2009, Stanislaus Madueke | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Licensed under the New BSD License | ||||
|  * See: http://www.opensource.org/licenses/bsd-license.php | ||||
|  */ | ||||
| ;(function($) { | ||||
|     $.fn.formset = function(opts) | ||||
|     { | ||||
|         var options = $.extend({}, $.fn.formset.defaults, opts), | ||||
|             flatExtraClasses = options.extraClasses.join(' '), | ||||
|             totalForms = $('#id_' + options.prefix + '-TOTAL_FORMS'), | ||||
|             maxForms = $('#id_' + options.prefix + '-MAX_NUM_FORMS'), | ||||
|             minForms = $('#id_' + options.prefix + '-MIN_NUM_FORMS'), | ||||
|             childElementSelector = 'input,select,textarea,label,div', | ||||
|             $$ = $(this), | ||||
|  | ||||
|             applyExtraClasses = function(row, ndx) { | ||||
|                 if (options.extraClasses) { | ||||
|                     row.removeClass(flatExtraClasses); | ||||
|                     row.addClass(options.extraClasses[ndx % options.extraClasses.length]); | ||||
|                 } | ||||
|             }, | ||||
|  | ||||
|             updateElementIndex = function(elem, prefix, ndx) { | ||||
|                 var idRegex = new RegExp(prefix + '-(\\d+|__prefix__)-'), | ||||
|                     replacement = prefix + '-' + ndx + '-'; | ||||
|                 if (elem.attr("for")) elem.attr("for", elem.attr("for").replace(idRegex, replacement)); | ||||
|                 if (elem.attr('id')) elem.attr('id', elem.attr('id').replace(idRegex, replacement)); | ||||
|                 if (elem.attr('name')) elem.attr('name', elem.attr('name').replace(idRegex, replacement)); | ||||
|             }, | ||||
|  | ||||
|             hasChildElements = function(row) { | ||||
|                 return row.find(childElementSelector).length > 0; | ||||
|             }, | ||||
|  | ||||
|             showAddButton = function() { | ||||
|                 return maxForms.length == 0 ||   // For Django versions pre 1.2 | ||||
|                     (maxForms.val() == '' || (maxForms.val() - totalForms.val() > 0)); | ||||
|             }, | ||||
|  | ||||
|             /** | ||||
|             * Indicates whether delete link(s) can be displayed - when total forms > min forms | ||||
|             */ | ||||
|             showDeleteLinks = function() { | ||||
|                 return minForms.length == 0 ||   // For Django versions pre 1.7 | ||||
|                     (minForms.val() == '' || (totalForms.val() - minForms.val() > 0)); | ||||
|             }, | ||||
|  | ||||
|             insertDeleteLink = function(row) { | ||||
|                 var delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.'), | ||||
|                     addCssSelector = $.trim(options.addCssClass).replace(/\s+/g, '.'); | ||||
|                 if (row.is('TR')) { | ||||
|                     // If the forms are laid out in table rows, insert | ||||
|                     // the remove button into the last table cell: | ||||
|                     row.children(':last').append('<a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + '</a>'); | ||||
|                 } else if (row.is('UL') || row.is('OL')) { | ||||
|                     // If they're laid out as an ordered/unordered list, | ||||
|                     // insert an <li> after the last list item: | ||||
|                     row.append('<li><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a></li>'); | ||||
|                 } else { | ||||
|                     // Otherwise, just insert the remove button as the | ||||
|                     // last child element of the form's container: | ||||
|                     row.append('<a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a>'); | ||||
|                 } | ||||
|                 // Check if we're under the minimum number of forms - not to display delete link at rendering | ||||
|                 if (!showDeleteLinks()){ | ||||
|                     row.find('a.' + delCssSelector).hide(); | ||||
|                 } | ||||
|  | ||||
|                 row.find('a.' + delCssSelector).click(function() { | ||||
|                     var row = $(this).parents('.' + options.formCssClass), | ||||
|                         del = row.find('input:hidden[id $= "-DELETE"]'), | ||||
|                         buttonRow = row.siblings("a." + addCssSelector + ', .' + options.formCssClass + '-add'), | ||||
|                         forms; | ||||
|                     if (del.length) { | ||||
|                         // We're dealing with an inline formset. | ||||
|                         // Rather than remove this form from the DOM, we'll mark it as deleted | ||||
|                         // and hide it, then let Django handle the deleting: | ||||
|                         del.val('on'); | ||||
|                         row.hide(); | ||||
|                         forms = $('.' + options.formCssClass).not(':hidden'); | ||||
|                     } else { | ||||
|                         row.remove(); | ||||
|                         // Update the TOTAL_FORMS count: | ||||
|                         forms = $('.' + options.formCssClass).not('.formset-custom-template'); | ||||
|                         totalForms.val(forms.length); | ||||
|                     } | ||||
|                     for (var i=0, formCount=forms.length; i<formCount; i++) { | ||||
|                         // Apply `extraClasses` to form rows so they're nicely alternating: | ||||
|                         applyExtraClasses(forms.eq(i), i); | ||||
|                         if (!del.length) { | ||||
|                             // Also update names and IDs for all child controls (if this isn't | ||||
|                             // a delete-able inline formset) so they remain in sequence: | ||||
|                             forms.eq(i).find(childElementSelector).each(function() { | ||||
|                                 updateElementIndex($(this), options.prefix, i); | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                     // Check if we've reached the minimum number of forms - hide all delete link(s) | ||||
|                     if (!showDeleteLinks()){ | ||||
|                         $('a.' + delCssSelector).each(function(){$(this).hide();}); | ||||
|                     } | ||||
|                     // Check if we need to show the add button: | ||||
|                     if (buttonRow.is(':hidden') && showAddButton()) buttonRow.show(); | ||||
|                     // If a post-delete callback was provided, call it with the deleted form: | ||||
|                     if (options.removed) options.removed(row); | ||||
|                     return false; | ||||
|                 }); | ||||
|             }; | ||||
|  | ||||
|         $$.each(function(i) { | ||||
|             var row = $(this), | ||||
|                 del = row.find('input:checkbox[id $= "-DELETE"]'); | ||||
|             if (del.length) { | ||||
|                 // If you specify "can_delete = True" when creating an inline formset, | ||||
|                 // Django adds a checkbox to each form in the formset. | ||||
|                 // Replace the default checkbox with a hidden field: | ||||
|                 if (del.is(':checked')) { | ||||
|                     // If an inline formset containing deleted forms fails validation, make sure | ||||
|                     // we keep the forms hidden (thanks for the bug report and suggested fix Mike) | ||||
|                     del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" value="on" />'); | ||||
|                     row.hide(); | ||||
|                 } else { | ||||
|                     del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" />'); | ||||
|                 } | ||||
|                 // Hide any labels associated with the DELETE checkbox: | ||||
|                 $('label[for="' + del.attr('id') + '"]').hide(); | ||||
|                 del.remove(); | ||||
|             } | ||||
|             if (hasChildElements(row)) { | ||||
|                 row.addClass(options.formCssClass); | ||||
|                 if (row.is(':visible')) { | ||||
|                     insertDeleteLink(row); | ||||
|                     applyExtraClasses(row, i); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         if ($$.length) { | ||||
|             var hideAddButton = !showAddButton(), | ||||
|                 addButton, template; | ||||
|             if (options.formTemplate) { | ||||
|                 // If a form template was specified, we'll clone it to generate new form instances: | ||||
|                 template = (options.formTemplate instanceof $) ? options.formTemplate : $(options.formTemplate); | ||||
|                 template.removeAttr('id').addClass(options.formCssClass + ' formset-custom-template'); | ||||
|                 template.find(childElementSelector).each(function() { | ||||
|                     updateElementIndex($(this), options.prefix, '__prefix__'); | ||||
|                 }); | ||||
|                 insertDeleteLink(template); | ||||
|             } else { | ||||
|                 // Otherwise, use the last form in the formset; this works much better if you've got | ||||
|                 // extra (>= 1) forms (thnaks to justhamade for pointing this out): | ||||
|                 template = $('.' + options.formCssClass + ':last').clone(true).removeAttr('id'); | ||||
|                 template.find('input:hidden[id $= "-DELETE"]').remove(); | ||||
|                 // Clear all cloned fields, except those the user wants to keep (thanks to brunogola for the suggestion): | ||||
|                 template.find(childElementSelector).not(options.keepFieldValues).each(function() { | ||||
|                     var elem = $(this); | ||||
|                     // If this is a checkbox or radiobutton, uncheck it. | ||||
|                     // This fixes Issue 1, reported by Wilson.Andrew.J: | ||||
|                     if (elem.is('input:checkbox') || elem.is('input:radio')) { | ||||
|                         elem.attr('checked', false); | ||||
|                     } else { | ||||
|                         elem.val(''); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|             // FIXME: Perhaps using $.data would be a better idea? | ||||
|             options.formTemplate = template; | ||||
|  | ||||
|             if ($$.is('TR')) { | ||||
|                 // If forms are laid out as table rows, insert the | ||||
|                 // "add" button in a new table row: | ||||
|                 var numCols = $$.eq(0).children().length,   // This is a bit of an assumption :| | ||||
|                     buttonRow = $('<tr><td colspan="' + numCols + '"><a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a></tr>') | ||||
|                                 .addClass(options.formCssClass + '-add'); | ||||
|                 $$.parent().append(buttonRow); | ||||
|                 if (hideAddButton) buttonRow.hide(); | ||||
|                 addButton = buttonRow.find('a'); | ||||
|             } else { | ||||
|                 // Otherwise, insert it immediately after the last form: | ||||
|                 $$.filter(':last').after('<a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a>'); | ||||
|                 addButton = $$.filter(':last').next(); | ||||
|                 if (hideAddButton) addButton.hide(); | ||||
|             } | ||||
|             addButton.click(function() { | ||||
|                 var formCount = parseInt(totalForms.val()), | ||||
|                     row = options.formTemplate.clone(true).removeClass('formset-custom-template'), | ||||
|                     buttonRow = $($(this).parents('tr.' + options.formCssClass + '-add').get(0) || this), | ||||
|                     delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.'); | ||||
|                 applyExtraClasses(row, formCount); | ||||
|                 row.insertBefore(buttonRow).show(); | ||||
|                 row.find(childElementSelector).each(function() { | ||||
|                     updateElementIndex($(this), options.prefix, formCount); | ||||
|                 }); | ||||
|                 totalForms.val(formCount + 1); | ||||
|                 // Check if we're above the minimum allowed number of forms -> show all delete link(s) | ||||
|                 if (showDeleteLinks()){ | ||||
|                     $('a.' + delCssSelector).each(function(){$(this).show();}); | ||||
|                 } | ||||
|                 // Check if we've exceeded the maximum allowed number of forms: | ||||
|                 if (!showAddButton()) buttonRow.hide(); | ||||
|                 // If a post-add callback was supplied, call it with the added form: | ||||
|                 if (options.added) options.added(row); | ||||
|                 return false; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         return $$; | ||||
|     }; | ||||
|  | ||||
|     /* Setup plugin defaults */ | ||||
|     $.fn.formset.defaults = { | ||||
|         prefix: 'form',                  // The form prefix for your django formset | ||||
|         formTemplate: null,              // The jQuery selection cloned to generate new form instances | ||||
|         addText: 'add another',          // Text for the add link | ||||
|         deleteText: 'remove',            // Text for the delete link | ||||
|         addCssClass: 'btn btn-primary',          // CSS class applied to the add link | ||||
|         deleteCssClass: 'btn btn-danger h-50 my-auto',    // CSS class applied to the delete link | ||||
|         formCssClass: 'dynamic-form',    // CSS class applied to each form in a formset | ||||
|         extraClasses: [],                // Additional CSS classes, which will be applied to each form in turn | ||||
|         keepFieldValues: '',             // jQuery selector for fields whose values should be kept when the form is cloned | ||||
|         added: null,                     // Function called each time a new form is added | ||||
|         removed: null                    // Function called each time a form is deleted | ||||
|     }; | ||||
| })(jQuery); | ||||
							
								
								
									
										23
									
								
								templates/member/add_members.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								templates/member/add_members.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| {% extends "base.html" %} | ||||
| {% load crispy_forms_tags %} | ||||
| {% load static %} | ||||
| {% block content %} | ||||
|  | ||||
| <form method="post" action=""> | ||||
|     {% csrf_token %} | ||||
|     {% crispy formset helper %} | ||||
|     <div class="form-actions"> | ||||
|         <input type="submit" name="submit" value="Save" class="btn btn-primary" id="submit-save"> | ||||
|     </div> | ||||
| </form> | ||||
|  | ||||
| <!-- Include formset plugin - including jQuery dependency --> | ||||
| <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> | ||||
| <script src="{% static 'js/dynamic-formset.js' %}"></script> | ||||
| <script> | ||||
|     $('.formset-row').formset({ | ||||
|         addText: 'add link', | ||||
|         deleteText: 'remove' | ||||
|     }); | ||||
| </script> | ||||
| {% endblock %} | ||||
| @@ -3,4 +3,6 @@ | ||||
| {% block content %} | ||||
| <p><a class="btn btn-default" href="{% url 'member:club_list' %}">Clubs</a></p> | ||||
| <h5>{{ object.name }}</h5> | ||||
|  | ||||
| <a class="btn btn-default" href="{% url 'member:club_add_member' pk=object.pk %}"> Ajouter des membres </a> | ||||
| {% endblock %} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user