Skip to content

Commit

Permalink
Merge pull request #11 from g1sky/support-for-django-2
Browse files Browse the repository at this point in the history
Added PopupViewModelField and support for django 2.0 and 2.1.
  • Loading branch information
djk2 authored Apr 19, 2019
2 parents 69eec16 + ce53271 commit d34b6b7
Show file tree
Hide file tree
Showing 23 changed files with 202 additions and 46 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ dist/
.tox/
eggs/
.eggs/
.idea/

# extends
# --------
Expand All @@ -15,6 +16,7 @@ eggs/
*.mo
*.pot
*.log
*.sqlite3

# files
# -----
Expand Down
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ matrix:
- { python: 3.6, env: TOXENV=py36-dj_1.10-bootstrap_8.2-crispy_1.6-tests }
- { python: 2.7, env: TOXENV=py27-dj_1.11-bootstrap_8.2-crispy_1.6-tests }
- { python: 3.6, env: TOXENV=py36-dj_1.11-bootstrap_8.2-crispy_1.6-tests }
- { python: 3.6, env: TOXENV=py36-dj_2.0-bootstrap_8.2-crispy_1.7-tests }
- { python: 3.6, env: TOXENV=py36-dj_2.1-bootstrap_8.2-crispy_1.7-tests }
- { python: 2.7, env: TOXENV=py27-flake }
- { python: 3.6, env: TOXENV=py36-flake }

Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# CHANGELOG for django-popup-view-field

## 0.5.0 (2019-04-17)

* Support for Django 2.0 and 2.1

* Added PopupViewModelField that returns a model instance instead of text

## 0.4.1 (2019-02-23)

* Added `attrs` attribute to PopupViewField which is passed to the Widget
Expand Down
133 changes: 127 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ You can create normal django View and load this view in dialog for form field.
- Support:

* Python: 2.7, 3.6
* Django: 1.8, 1.9, 1.10, 1.11
* Django: 1.8, 1.9, 1.10, 1.11, 2.0, 2.1
* django-crispy-forms
* django-bootstrap3

Expand Down Expand Up @@ -59,10 +59,10 @@ You can create normal django View and load this view in dialog for form field.
Screenshots
------------

- Example: Form with several popup-view-fieds
- Example: Form with several popup-view-fields

.. image:: https://raw.githubusercontent.com/djk2/django-popup-view-field/master/doc/static/scr1.png
:alt: Form with django-popup-view-fieds
:alt: Form with django-popup-view-fields

- Example: Dialog for select sex

Expand Down Expand Up @@ -170,7 +170,7 @@ Add the django_popup_view_field urls to your root url patterns
In your base template, add ``django_popup_view_field_javascript`` tag
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``django_popup_view_field_javascript`` template tag load all required javascripts and
template-scripst for application.
template-scripts for application.
Tag should be append before body close </body> tag and after jQuery and Bootstrap scripts.

*base.html* ::
Expand Down Expand Up @@ -414,7 +414,7 @@ PopupView
Reverse order
</a>

**popups.py* ::
*popups.py* ::

from django.views.generic import TemplateView
from django_popup_view_field.registry import registry_popup_view
Expand Down Expand Up @@ -475,10 +475,131 @@ View
form_class = AlphabetForm

def form_valid(self, form):
char = form.cleande_data.get("char")
char = form.cleaned_data.get("char")
return HttpResponse("First letter of your name : {0}".format(char))


PopupViewModelField Example
-----------------------------
``PopupViewModelField`` allows you to send model objects through the form inheriting from ``ModelForm``.

.. image:: https://raw.githubusercontent.com/djk2/django-popup-view-field/master/doc/static/PopupViewModelField_example.png
:alt: PopupViewModelField Example - screenshot


Model
^^^^^^^
*models.py* ::

from django.db import models


class Country(models.Model):
code = models.CharField(max_length=2, primary_key=True)
name = models.CharField(max_length=256)


class ExampleUser(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
country_code = models.ForeignKey(Country, on_delete=models.PROTECT)


PopupView
^^^^^^^^^^
*templates/myapp/popups/country.html* ::

<ul>
{% for country in countries %}
<li style="cursor:pointer" data-popup-view-value="{{ country.code }}">
<strong>{{ country.code }}</strong> - {{ country.name }}
</li>
{% endfor %}
<ul>

**Note:**
``data-popup-view-value`` attribute should return the primary key value of the model object.

*popups.py* ::

class CountryPopupView(TemplateView):

template_name = "myapp/popups/country.html"
countries = None

def get(self, request, *args, **kwargs):
self.countries = Country.objects.all()
return super(CountryPopupView, self).get(request, *args, **kwargs)

def get_context_data(self, **kwargs):
context = super(CountryPopupView, self).get_context_data(**kwargs)
context['countries'] = self.countries
return context

# REGISTER IS IMPORTANT
registry_popup_view.register(CountryPopupView)


Form with PopupViewModelField
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*forms.py* ::

from django import forms
from django.forms import ModelForm
from .models import Country, ExampleUser
from django_popup_view_field.fields import PopupViewModelField

class CountryForm(ModelForm):

country_code = PopupViewModelField(
queryset=Country.objects.all(),
view_class=CountryPopupView,
required=True
)

class Meta:
model = ExampleUser
fields = ('first_name', 'last_name', 'country_code')

**Note:**
``PopupViewModelField`` must have an additional ``queryset`` argument in which we pass model objects.


View
^^^^^
*templates/myapp/country.html* ::

{% extends "base.html" %}
{% load bootstrap3 %}

<form action="." method="post" class="form">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">Submit</button>
{% endbuttons %}
</form>

*views.py* ::

from django.views.generic import FormView
from myapp.forms import CountryForm
from django.contrib import messages

class CountryFormView(FormView):
template_name = "myapp/country.html"
form_class = CountryForm

def form_valid(self, form):
ret = super(CountryFormView, self).form_valid(form)
country_code = form.cleaned_data.get("country_code")
messages.success(
self.request,
"Success in create example user with code of country: {0}".format(country_code)
)
return ret


Others
---------
* Remember, if you use a django-crispy-forms then you should set CRISPY_TEMPLATE_PACK = "bootstrap3" in settings.py
Expand Down
Binary file modified demo/db.sqlite3
Binary file not shown.
5 changes: 3 additions & 2 deletions demo/demo/forms.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from crispy_forms.helper import FormHelper
from django import forms

from django_popup_view_field.fields import PopupViewField
from .models import Country
from django_popup_view_field.fields import PopupViewField, PopupViewModelField

from .popups import ColorPopupView, CountryPopupView, SexPopupView

Expand All @@ -20,7 +21,7 @@ class DemoForm(forms.Form):
)

color = PopupViewField(view_class=ColorPopupView)
country_code = PopupViewField(view_class=CountryPopupView)
country_code = PopupViewModelField(queryset=Country.objects.all(), view_class=CountryPopupView)

def __init__(self, *args, **kwargs):
super(DemoForm, self).__init__(*args, **kwargs)
Expand Down
6 changes: 1 addition & 5 deletions demo/demo/popups.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
from string import ascii_uppercase

from django import forms
from django.http import HttpResponse
from django.views.generic import FormView, TemplateView
from django.views.generic import TemplateView

from django_popup_view_field.registry import registry_popup_view

from .models import Country


Expand Down
2 changes: 1 addition & 1 deletion demo/demo/templates/demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

{% block content %}

<h1> Demo - django-popuop-view-field </h1>
<h1> Demo - django-popup-view-field </h1>

<div>
<ul class="nav nav-tabs" role="tablist" id="tabs" data-toggle="tab">
Expand Down
3 changes: 1 addition & 2 deletions demo/demo/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from django.http import HttpResponse
from django.views.generic import CreateView, TemplateView
from django.views.generic import TemplateView

from .forms import DemoForm

Expand Down
2 changes: 1 addition & 1 deletion demo/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Django==1.11.15
Django==2.1.8
django-bootstrap3==11.0.0
django-crispy-forms==1.7.2
../.
2 changes: 1 addition & 1 deletion django_popup_view_field/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
VERSION = (0, 4, 1)
VERSION = (0, 5, 0)
__version__ = ".".join(str(i) for i in VERSION)
6 changes: 3 additions & 3 deletions django_popup_view_field/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
class PopupViewIsNotSubclassView(Exception):
"Exception when view_class for field not inherit from django.views.generic.View"
"""Exception when view_class for field not inherit from django.views.generic.View"""


class PopupViewAlreadyRegistered(Exception):
"Exception when trying to register a view which is already registered."
"""Exception when trying to register a view which is already registered."""


class PopupViewNotRegistered(Exception):
"Exception when trying use a lookup which is not registered."
"""Exception when trying use a lookup which is not registered."""
48 changes: 37 additions & 11 deletions django_popup_view_field/fields.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import urllib

from django.forms import ModelChoiceField
from django.forms.fields import CharField
from django.utils.translation import ugettext_lazy as _
from django.views.generic import View
Expand All @@ -8,9 +9,12 @@
from .widgets import PopupViewWidget


class PopupViewField(CharField):
class PopupViewFieldMixin(object):
view_class_name = None
popup_dialog_title = None
callback_data = None

def __init__(self, view_class, attrs=None, *args, **kwargs):
def validate_arguments(self, view_class, kwargs):
"""
view_class : View Class used to render content popup dialog
view_class must be subclass of django.views.generic.View
Expand All @@ -20,22 +24,44 @@ def __init__(self, view_class, attrs=None, *args, **kwargs):
if not issubclass(view_class, View):
raise PopupViewIsNotSubclassView()

view_class_name = view_class.__name__
popup_dialog_title = kwargs.pop("popup_dialog_title", _("Popup Dialog: Select value"))
self.view_class_name = view_class.__name__
self.popup_dialog_title = kwargs.pop("popup_dialog_title", _("Popup Dialog: Select value"))

callback_data = kwargs.pop("callback_data", {})
if not isinstance(callback_data, dict):
self.callback_data = kwargs.pop("callback_data", {})
if not isinstance(self.callback_data, dict):
raise AttributeError("callback_data argument must be a dictionary")
try:
callback_data = urllib.urlencode(callback_data)
self.callback_data = urllib.urlencode(self.callback_data)
except AttributeError:
callback_data = urllib.parse.urlencode(callback_data)
self.callback_data = urllib.parse.urlencode(self.callback_data)


class PopupViewField(CharField, PopupViewFieldMixin):

def __init__(self, view_class, attrs=None, *args, **kwargs):
self.validate_arguments(view_class, kwargs)
super(PopupViewField, self).__init__(
widget=PopupViewWidget(
view_class_name=view_class_name,
popup_dialog_title=popup_dialog_title,
callback_data=callback_data,
view_class_name=self.view_class_name,
popup_dialog_title=self.popup_dialog_title,
callback_data=self.callback_data,
attrs=attrs
),
*args,
**kwargs
)


class PopupViewModelField(ModelChoiceField, PopupViewFieldMixin):

def __init__(self, view_class, queryset, attrs=None, *args, **kwargs):
self.validate_arguments(view_class, kwargs)
super(PopupViewModelField, self).__init__(
queryset,
widget=PopupViewWidget(
view_class_name=self.view_class_name,
popup_dialog_title=self.popup_dialog_title,
callback_data=self.callback_data,
attrs=attrs
),
*args,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from django.template import Context, Library, loader
from django.utils.safestring import mark_safe
from django.template import Library, loader

register = Library()

Expand Down
3 changes: 0 additions & 3 deletions django_popup_view_field/tests.py
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
from django.test import TestCase

# Create your tests here.
6 changes: 4 additions & 2 deletions django_popup_view_field/tests/test_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@
from collections import OrderedDict

from django import VERSION, forms
from django.forms import ModelChoiceField
from django.forms.fields import CharField
from django.test import TestCase
from django.views.generic import View

from django_popup_view_field.fields import PopupViewField
from django_popup_view_field.fields import PopupViewField, PopupViewFieldMixin, PopupViewModelField


class FieldTest(TestCase):

def test_subclass_field(self):
assert issubclass(PopupViewField, CharField) is True
assert issubclass(PopupViewField, (CharField, PopupViewFieldMixin)) is True
assert issubclass(PopupViewModelField, (ModelChoiceField, PopupViewFieldMixin)) is True

def test_form(self):

Expand Down
2 changes: 1 addition & 1 deletion django_popup_view_field/tests/test_templatetags.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# encoding: utf-8
from django.template import Context, engines
from django.template import engines
from django.test import TestCase


Expand Down
Loading

0 comments on commit d34b6b7

Please sign in to comment.