Skip to content

Commit

Permalink
Merge pull request #3 from wsmith323/add-django-text-field
Browse files Browse the repository at this point in the history
Add django text field
  • Loading branch information
wsmith323 committed Jul 13, 2020
2 parents 7311449 + b3c3f65 commit be96e06
Show file tree
Hide file tree
Showing 13 changed files with 412 additions and 27 deletions.
Empty file added django_test_app/__init__.py
Empty file.
31 changes: 31 additions & 0 deletions django_test_app/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from django.db import models

from staticmodel import StaticModel
from staticmodel.django.fields import (
StaticModelCharField, StaticModelIntegerField,
StaticModelTextField,
)


class String(StaticModel):
_field_names = 'code', 'display'
VALUE_1 = 'value_1', 'String Value 1'
VALUE_2 = 'value_2', 'String Value 2'
VALUE_3 = 'value_3', 'String Value 3'


class Integer(StaticModel):
_field_names = 'value', 'display'
VALUE_1 = 1, 'Int Value 1'
VALUE_2 = 2, 'Int Value 2'
VALUE_3 = 3, 'Int Value 3'


class TestModel(models.Model):
name = models.CharField(max_length=10, unique=True)
char = StaticModelCharField(static_model=String, value_field_name='code',
display_field_name='display', max_length=10, null=True)
text = StaticModelTextField(static_model=String, value_field_name='code',
display_field_name='display', null=True)
integer = StaticModelIntegerField(static_model=Integer, value_field_name='value',
display_field_name='display', null=True)
122 changes: 122 additions & 0 deletions django_test_app/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""
Django settings for django_test_app project.
Generated by 'django-admin startproject' using Django 1.11.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 's0i-g_e=5&d(^nl_oca(dypm9se21gsvm50@l=_x0tpobvb9@b'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
# 'django.contrib.admin',
# 'django.contrib.auth',
# 'django.contrib.contenttypes',
# 'django.contrib.sessions',
# 'django.contrib.messages',
# 'django.contrib.staticfiles',

'django_test_app',
]

# MIDDLEWARE = [
# 'django.middleware.security.SecurityMiddleware',
# 'django.contrib.sessions.middleware.SessionMiddleware',
# 'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
# 'django.contrib.auth.middleware.AuthenticationMiddleware',
# 'django.contrib.messages.middleware.MessageMiddleware',
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
# ]

# ROOT_URLCONF = 'django_test_app.urls'

# TEMPLATES = [
# {
# 'BACKEND': 'django.template.backends.django.DjangoTemplates',
# 'DIRS': [],
# 'APP_DIRS': True,
# 'OPTIONS': {
# 'context_processors': [
# 'django.template.context_processors.debug',
# 'django.template.context_processors.request',
# 'django.contrib.auth.context_processors.auth',
# 'django.contrib.messages.context_processors.messages',
# ],
# },
# },
# ]

WSGI_APPLICATION = 'django_test_app.wsgi.application'


# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}


# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators

# AUTH_PASSWORD_VALIDATORS = [
# {
# 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
# },
# {
# 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
# },
# {
# 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
# },
# {
# 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
# },
# ]


# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

# STATIC_URL = '/static/'
21 changes: 21 additions & 0 deletions django_test_app/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""django_integration URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin

urlpatterns = [
url(r'^admin/', admin.site.urls),
]
16 changes: 16 additions & 0 deletions django_test_app/wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
WSGI config for django_integration project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_integration.settings")

application = get_wsgi_application()
22 changes: 22 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_test_app.settings")
try:
from django.core.management import execute_from_command_line
except ImportError:
# The above import may fail for some other reason. Ensure that the
# issue is really that Django is missing to avoid masking other
# exceptions on Python 2.
try:
import django
except ImportError:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
)
raise
execute_from_command_line(sys.argv)
7 changes: 7 additions & 0 deletions run_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -e

python setup.py test

pip install 'django>=1.11,<2.0'
./manage.py test
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def file_read(filename):
setup(
name="staticmodel",
version=__version__,
packages=find_packages(exclude=['tests', 'docs']),
packages=find_packages(
exclude=['docs', 'tests', 'test_django_integration', 'django_test_app']),
include_package_data=True,
install_requires=['six'],
author="Warren A. Smith",
Expand Down
4 changes: 2 additions & 2 deletions staticmodel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,12 +712,12 @@ class and then define our methods on it. *Is there a simpler way?*
... AnimalType.members.get(name='Eagle')
... except AnimalType.DoesNotExist as e:
... print(e)
AnimalType.get(name='Eagle') yielded no objects.
AnimalType.members.get(name='Eagle') yielded no objects.
>>> try:
... AnimalType.members.get(domesticated=True)
... except AnimalType.MultipleObjectsReturned as e:
... print(e)
AnimalType.get(domesticated=True) yielded multiple objects.
AnimalType.members.get(domesticated=True) yielded multiple objects.
----------------------
The _member_name field
Expand Down
4 changes: 2 additions & 2 deletions staticmodel/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,14 +281,14 @@ def get(self, _return_none=False, **kwargs):
return None
else:
raise self.model.DoesNotExist(
'{}.get({}) yielded no objects.'.format(
'{}.members.get({}) yielded no objects.'.format(
self.model.__name__, format_kwargs(kwargs)))

elif len(results) == 1:
return results[0]
else:
raise self.model.MultipleObjectsReturned(
'{}.get({}) yielded multiple objects.'.format(
'{}.members.get({}) yielded multiple objects.'.format(
self.model.__name__, format_kwargs(kwargs)))


Expand Down
65 changes: 43 additions & 22 deletions staticmodel/django/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __init__(self, *args, **kwargs):
self._static_model._field_names[0])
self._display_field_name = kwargs.pop('display_field_name',
self._value_field_name)
self._validate_field_values()
self._validate_field_values(*args, **kwargs)

kwargs['choices'] = tuple(self._static_model.members.all().values_list(
self._value_field_name, self._display_field_name))
Expand Down Expand Up @@ -42,41 +42,41 @@ def deconstruct(self):
display_field_name=self._display_field_name,
))

del kwargs["choices"]
# del kwargs["choices"]

return name, path, args, kwargs

def _validate_field_values(self):
def _validate_field_values(self, *constructor_args, **constructor_kwargs):
for member in self._static_model.members.all():
value = getattr(member, self._value_field_name, None)
self._validate_member_value(member, value)
self._validate_member_value(member, value, *constructor_args, **constructor_kwargs)

display_value = getattr(member, self._display_field_name, None)
if not isinstance(display_value, six.string_types):
raise ValueError(
'Field {!r} of member {!r} must be a string.'.format(
self._display_field_name, member._member_name))

def _validate_member_value(self, member, value):
def _validate_member_value(self, member, value, *constructor_args, **constructor_kwargs):
raise NotImplementedError

def get_prep_value(self, member):
return super(StaticModelFieldMixin, self).get_prep_value(
getattr(member, self._value_field_name))

def to_python(self, db_value):
super_value = super(StaticModelFieldMixin, self).to_python(db_value)
try:
return self._static_model.members.get(
**{self._value_field_name: super_value})
except self._static_model.DoesNotExist:
return None
def get_prep_value(self, value):
if isinstance(value, self._static_model):
return getattr(value, self._value_field_name)
else:
return value

def from_db_value(self, value, expression, connection, context):
try:
if value is None:
return value
else:
return self._static_model.members.get(**{self._value_field_name: value})
except self._static_model.DoesNotExist:
return None

def to_python(self, db_value):
if db_value is None or isinstance(db_value, self._static_model):
return db_value
else:
return self._static_model.members.get(**{self._value_field_name: db_value})

def contribute_to_class(self, cls, name, **kwargs):
super(StaticModelFieldMixin, self).contribute_to_class(cls, name, **kwargs)
Expand All @@ -88,15 +88,36 @@ def _get_FIELD_display(instance):
setattr(cls, 'get_{}_display'.format(self.name), _get_FIELD_display)


class StaticModelCharField(StaticModelFieldMixin, models.CharField):
def _validate_member_value(self, member, value):
class StaticModelStringFieldMixin(StaticModelFieldMixin):
def _validate_member_value(self, member, value, *constructor_args, **constructor_kwargs):
if not isinstance(value, six.string_types):
raise ValueError('Field {!r} of member {!r} must be a string.'.format(
self._value_field_name, member._member_name))


class StaticModelCharField(StaticModelStringFieldMixin, models.CharField):
def get_internal_type(self):
return 'CharField'

def _validate_member_value(self, member, value, *constructor_args, **constructor_kwargs):
super(StaticModelCharField, self)._validate_member_value(
member, value, *constructor_args, **constructor_kwargs)
max_length = constructor_kwargs.get('max_length')
if max_length is not None and len(value) > max_length:
raise ValueError('Length of field {!r} of member {!r} must be <= {}'.format(
self._value_field_name, member._member_name, max_length))


class StaticModelTextField(StaticModelStringFieldMixin, models.TextField):
def get_internal_type(self):
return 'TextField'


class StaticModelIntegerField(StaticModelFieldMixin, models.IntegerField):
def _validate_member_value(self, member, value):
def get_internal_type(self):
return 'IntegerField'

def _validate_member_value(self, member, value, *constructor_args, **constructor_kwargs):
if not isinstance(value, six.integer_types):
raise ValueError('Field {!r} of member {!r} must be an integer.'.format(
self._value_field_name, member._member_name))
Expand Down
Empty file.

0 comments on commit be96e06

Please sign in to comment.