Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b9e4ad7
Fix waring from BSModalDeleteView / DeleteMessageMixin with Django 4
christianwgd Apr 19, 2022
53ca042
fix when form has field named method
markmono Mar 18, 2023
8699c8c
add new mixin for FormValidation, remove SuccessMessageMixin dependecy
trco Mar 19, 2023
aec0024
Updated project to current Django LTS version
Apr 9, 2023
ea9cba9
Minor refactoring
Apr 9, 2023
a1ce488
Updated Project
Apr 11, 2023
4e01389
Removed unused constant
Apr 11, 2023
3822486
Updated required version
Apr 11, 2023
a663e7a
Minor Bugfix
Apr 13, 2023
17ac8cf
refactor save method in CreateUpdateAjaxMixin
trco Apr 30, 2023
2c83a7e
remove compatibility.py
trco Apr 30, 2023
fb09568
remove utils.py
trco Apr 30, 2023
ffe2a5f
remove types
trco Apr 30, 2023
d5e2e69
add is_ajax method and remove imports
trco Apr 30, 2023
0c7da27
Merge branch 'improve-requests' into update_project
trco Apr 30, 2023
8a1822c
remove unneeded class inheritence
trco Apr 30, 2023
2d2017a
remove obsolete class name parameter
trco Apr 30, 2023
82b7a2c
revert examples to version in master branch
trco Apr 30, 2023
12c206d
remove static folder
trco Apr 30, 2023
28c706e
remove types from tests
trco Apr 30, 2023
9cf89fc
remove unneeded comments
trco May 1, 2023
5e83a52
Merge branch 'pr-210' into update_project
trco May 1, 2023
8492ffc
update get and set for form action and method attributes
trco May 1, 2023
373b159
update bootstrap5.modal.forms.min.js
trco May 1, 2023
c4d3f84
Merge branch 'pr-195' into update_project
trco May 1, 2023
db39502
update assert string to pass the test
trco May 1, 2023
bd7b6b4
update DeleteMessageMixin comment
trco May 1, 2023
bae3e6c
cleanup .gitignore
trco May 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ database/db.sqlite3
geckodriver.log
__pycache__
*.pyc
.env/
.env/
84 changes: 0 additions & 84 deletions bootstrap_modal_forms/compatibility.py

This file was deleted.

20 changes: 5 additions & 15 deletions bootstrap_modal_forms/generic.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,22 @@
import django
from django.contrib.messages.views import SuccessMessageMixin
from django.views import generic
from .mixins import PassRequestMixin, DeleteMessageMixin, LoginAjaxMixin
from django.contrib.auth.views import LoginView

DJANGO_VERSION = django.get_version().split('.')
DJANGO_MAJOR_VERSION = DJANGO_VERSION[0]
DJANGO_MINOR_VERSION = DJANGO_VERSION[1]
from .mixins import PassRequestMixin, DeleteMessageMixin, LoginAjaxMixin, FormValidationMixin

# Import custom LoginView for Django versions < 1.11
if DJANGO_MAJOR_VERSION == '1' and '11' not in DJANGO_MINOR_VERSION:
from .compatibility import LoginView
else:
from django.contrib.auth.views import LoginView


class BSModalLoginView(LoginAjaxMixin, SuccessMessageMixin, LoginView):
class BSModalLoginView(LoginAjaxMixin, LoginView):
pass


class BSModalFormView(PassRequestMixin, generic.FormView):
pass


class BSModalCreateView(PassRequestMixin, SuccessMessageMixin, generic.CreateView):
class BSModalCreateView(PassRequestMixin, FormValidationMixin, generic.CreateView):
pass


class BSModalUpdateView(PassRequestMixin, SuccessMessageMixin, generic.UpdateView):
class BSModalUpdateView(PassRequestMixin, FormValidationMixin, generic.UpdateView):
pass


Expand Down
79 changes: 51 additions & 28 deletions bootstrap_modal_forms/mixins.py
Original file line number Diff line number Diff line change
@@ -1,73 +1,96 @@
from django.contrib import messages
from django.contrib.auth import login as auth_login
from django.http import HttpResponseRedirect
from django.http import HttpResponseRedirect, HttpResponse

from .utils import is_ajax


class PassRequestMixin(object):
class PassRequestMixin:
"""
Mixin which puts the request into the form's kwargs.
Form Mixin which puts the request into the form's kwargs.

Note: Using this mixin requires you to pop the `request` kwarg
out of the dict in the super of your form's `__init__`.
"""

def get_form_kwargs(self):
kwargs = super(PassRequestMixin, self).get_form_kwargs()
kwargs.update({'request': self.request})
kwargs = super().get_form_kwargs()
kwargs['request'] = self.request
return kwargs


class PopRequestMixin(object):
class PopRequestMixin:
"""
Mixin which pops request out of the kwargs and attaches it to the form's
Form Mixin which pops request out of the kwargs and attaches it to the form's
instance.

Note: This mixin must precede forms.ModelForm/forms.Form. The form is not
expecting these kwargs to be passed in, so they must be popped off before
anything else is done.
"""

def __init__(self, *args, **kwargs):
def __init__(self, *args, **kwargs) -> None:
self.request = kwargs.pop('request', None)
super(PopRequestMixin, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)


class CreateUpdateAjaxMixin(object):
class CreateUpdateAjaxMixin:
"""
Mixin which passes or saves object based on request type.
ModelForm Mixin which passes or saves object based on request type.
"""

def save(self, commit=True):
if not is_ajax(self.request.META) or self.request.POST.get('asyncUpdate') == 'True':
instance = super(CreateUpdateAjaxMixin, self).save(commit=commit)
else:
instance = super(CreateUpdateAjaxMixin, self).save(commit=False)
return instance
isAjaxRequest = is_ajax(self.request.META)
asyncUpdate = self.request.POST.get('asyncUpdate') == 'True'

if not isAjaxRequest or asyncUpdate:
return super().save(commit=commit)
if isAjaxRequest:
return super().save(commit=False)


class DeleteMessageMixin(object):
class DeleteMessageMixin:
"""
Mixin which adds message to BSModalDeleteView and only calls the delete method if request
is not ajax request.
Generic View Mixin which adds message to BSModalDeleteView and only calls the post method if request
is not ajax request. In case request is ajax post method calls delete method, which redirects to success url.
"""
def delete(self, request, *args, **kwargs):

def post(self, request, *args, **kwargs):
if not is_ajax(request.META):
messages.success(request, self.success_message)
return super(DeleteMessageMixin, self).delete(request, *args, **kwargs)
return super().post(request, *args, **kwargs)
else:
self.object = self.get_object()
return HttpResponseRedirect(self.get_success_url())

class LoginAjaxMixin(object):

class LoginAjaxMixin:
"""
Mixin which authenticates user if request is not ajax request.
Generic View Mixin which authenticates user if request is not ajax request.
"""

def form_valid(self, form):
if not is_ajax(self.request.META):
auth_login(self.request, form.get_user())
messages.success(self.request, self.success_message)
return HttpResponseRedirect(self.get_success_url())
return HttpResponseRedirect(self.get_success_url())


class FormValidationMixin:
"""
Generic View Mixin which saves object and redirects to success_url if request is not ajax request. Otherwise response 204 No content is returned.
"""

def form_valid(self, form):
isAjaxRequest = is_ajax(self.request.META)
asyncUpdate = self.request.POST.get('asyncUpdate') == 'True'

if isAjaxRequest:
if asyncUpdate:
form.save()
return HttpResponse(status=204)

form.save()
messages.success(self.request, self.success_message)
return HttpResponseRedirect(self.success_url)


def is_ajax(meta):
return 'HTTP_X_REQUESTED_WITH' in meta and meta['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'
15 changes: 7 additions & 8 deletions bootstrap_modal_forms/static/js/bootstrap5.modal.forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const modalFormCallback = function (settings) {

let form = modal.querySelector(settings.modalForm);
if (form) {
form.action = settings.formURL;
form.setAttribute("action", settings.formURL);
addEventHandlers(modal, form, settings)
}
});
Expand Down Expand Up @@ -57,9 +57,9 @@ const isFormValid = function (settings, callback) {

let btnSubmit = modal.querySelector('button[type="submit"]');
btnSubmit.disabled = true;
fetch(form.action, {
fetch(form.getAttribute("action"), {
headers: headers,
method: form.method,
method: form.getAttribute("method"),
body: new FormData(form),
}).then(res => {
return res.text();
Expand All @@ -73,7 +73,7 @@ const isFormValid = function (settings, callback) {
return;
}

form.action = settings.formURL;
form.setAttribute("action", settings.formURL);
addEventHandlers(modal, form, settings)
} else {
callback(settings);
Expand All @@ -97,8 +97,8 @@ const submitForm = function (settings) {
// Add asyncUpdate and check for it in save method of CreateUpdateAjaxMixin
formData.append("asyncUpdate", "True");

fetch(form.action, {
method: form.method,
fetch(form.getAttribute("action"), {
method: form.getAttribute("method"),
body: formData,
}).then(res => {
return res.text();
Expand Down Expand Up @@ -142,7 +142,7 @@ const submitForm = function (settings) {
return;
}

form.action = settings.formURL;
form.setAttribute("action", settings.formURL);
addEventHandlers(modal, form, settings)
});
}
Expand All @@ -156,7 +156,6 @@ const submitForm = function (settings) {
};

const validateAsyncSettings = function (settings) {
console.log(settings)
var missingSettings = [];

if (!settings.successMessage) {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 0 additions & 8 deletions bootstrap_modal_forms/utils.py

This file was deleted.

3 changes: 0 additions & 3 deletions examples/admin.py

This file was deleted.

2 changes: 1 addition & 1 deletion examples/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@


class ExamplesConfig(AppConfig):
name = 'examples'
name = 'examples'
2 changes: 1 addition & 1 deletion examples/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ class Meta:
class CustomAuthenticationForm(AuthenticationForm):
class Meta:
model = User
fields = ['username', 'password']
fields = ['username', 'password']
Loading