Skip to content
This repository was archived by the owner on Mar 20, 2024. It is now read-only.

Commit 14f86ad

Browse files
author
dbate
committed
Added in IterField
1 parent eaba8bc commit 14f86ad

File tree

10 files changed

+247
-5
lines changed

10 files changed

+247
-5
lines changed

django_cookbook/model/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__author__ = 'dbate'

django_cookbook/model/fields.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import json
2+
from django.core.exceptions import ValidationError
3+
from django.db.models import TextField
4+
import re
5+
6+
7+
def is_iterable(obj):
8+
try:
9+
iter(obj)
10+
return True
11+
except TypeError:
12+
return False
13+
14+
15+
class IterField(TextField):
16+
"""
17+
Stores the an iterable object in the the database. The data is stored as JSON and so all the values given to the
18+
field must be serializable by the "json" module. This includes dict, list, tuple, str, int, float, True, False and
19+
None
20+
"""
21+
22+
LIST_RE = re.compile(r"\[(.*)\]")
23+
24+
def to_python(self, value):
25+
if isinstance(value, list):
26+
return value
27+
28+
if not isinstance(value, str):
29+
raise ValidationError("Invalid input for a IterField instance")
30+
31+
if not value:
32+
return []
33+
34+
# We could store the data as a string representation of the iterable which we then "eval" but this would allow
35+
# for malicious data to be stores in the field so we need to do some sanity checking on the string. We let the
36+
# json library handle this.
37+
return json.loads(value)
38+
39+
def get_prep_value(self, value):
40+
return json.dumps(value, separators=(',', ':'))
41+
42+

django_cookbook/test/__init__.py

Whitespace-only changes.

django_cookbook/test/settings.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""
2+
Django settings for quokka project.
3+
4+
For more information on this file, see
5+
https://docs.djangoproject.com/en/dev/topics/settings/
6+
7+
For the full list of settings and their values, see
8+
https://docs.djangoproject.com/en/dev/ref/settings/
9+
"""
10+
11+
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
12+
import os
13+
from django.conf.global_settings import MEDIA_ROOT
14+
import sys
15+
16+
import socket
17+
18+
try:
19+
HOSTNAME = socket.gethostname()
20+
except:
21+
HOSTNAME = 'localhost'
22+
23+
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
24+
25+
26+
# Quick-start development settings - unsuitable for production
27+
# See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/
28+
29+
# SECURITY WARNING: keep the secret key used in production secret!
30+
SECRET_KEY = '!j!rcmgm!@*zhjkq)3tl*r&&zug3&4hklo*s)#b*5_-=u0s1iw'
31+
32+
# SECURITY WARNING: don't run with debug turned on in production!
33+
DEBUG = True
34+
35+
TEMPLATE_DEBUG = True
36+
37+
ALLOWED_HOSTS = []
38+
39+
40+
# Application definition
41+
42+
INSTALLED_APPS = (
43+
'django.contrib.admin',
44+
'django.contrib.auth',
45+
'django.contrib.contenttypes',
46+
'django.contrib.sessions',
47+
'django.contrib.messages',
48+
'django.contrib.staticfiles',
49+
)
50+
51+
MIDDLEWARE_CLASSES = (
52+
'django.contrib.sessions.middleware.SessionMiddleware',
53+
'django.middleware.common.CommonMiddleware',
54+
'django.middleware.csrf.CsrfViewMiddleware',
55+
'django.contrib.auth.middleware.AuthenticationMiddleware',
56+
'django.contrib.messages.middleware.MessageMiddleware',
57+
'django.middleware.clickjacking.XFrameOptionsMiddleware',
58+
)
59+
60+
61+
62+
DATABASES = {
63+
'default': {
64+
'ENGINE': 'django.db.backends.sqlite3',
65+
'NAME': ':memory:',
66+
}
67+
}
68+
69+
# Internationalization
70+
# https://docs.djangoproject.com/en/dev/topics/i18n/
71+
72+
LANGUAGE_CODE = 'en-us'
73+
74+
TIME_ZONE = 'UTC'
75+
76+
USE_I18N = True
77+
78+
USE_L10N = True
79+
80+
USE_TZ = True

django_cookbook/test/unit/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__author__ = 'dbate'
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import os
2+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_cookbook.test.settings")
3+
4+
from django_cookbook.model.fields import IterField
5+
from django.test import TestCase
6+
7+
8+
class IterField_GetPrepValue(TestCase):
9+
def test_ValueIsAnEmptyList_StringIsEmptyList(self):
10+
field = IterField()
11+
12+
self.assertEqual("[]", field.get_prep_value([]))
13+
14+
def test_ValueIsAnEmptyDictionary_StringIsEmptyDict(self):
15+
field = IterField()
16+
17+
self.assertEqual("{}", field.get_prep_value({}))
18+
19+
def test_ValueIsListOfElements_StringIsJsonRepresentationOfThatListWithNoSpaces(self):
20+
field = IterField()
21+
22+
self.assertEqual("[1,2,3,4,5]", field.get_prep_value([1, 2, 3, 4, 5]))
23+
24+
def test_ValueIsListOfListsAndDictionaries_StringIsJsonRepresentationOfThatListWithNoSpaces(self):
25+
field = IterField()
26+
27+
self.assertEqual("[[1,2,3],{\"4\":5}]", field.get_prep_value([[1, 2, 3], {4: 5}]))
28+
29+
def test_ValueIsDictionaryOfElements_StringIsJsonRepresentationOfThatListWithNoSpaces(self):
30+
field = IterField()
31+
32+
self.assertEqual("{\"1\":2,\"3\":4,\"5\":6}", field.get_prep_value({1: 2, 3: 4, 5: 6}))
33+
34+
def test_ValueIsDictionaryOfListsAndDictionaries_StringIsJsonRepresentationOfThatListWithNoSpaces(self):
35+
field = IterField()
36+
37+
self.assertEqual("{\"1\":{\"2\":3},\"4\":[5,6]}", field.get_prep_value({1: {2: 3}, 4: [5, 6]}))
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from django.test import TestCase
2+
from django_cookbook.model.fields import IterField
3+
4+
5+
class IterField_PrepToPython(TestCase):
6+
def test_ValueIsEmptyList_ResultMatchesInput(self):
7+
value = []
8+
field = IterField()
9+
10+
prepped = field.get_prep_value(value)
11+
py = field.to_python(prepped)
12+
13+
self.assertEqual(value, py)
14+
15+
def test_ValueIsListWithElementsListAndDicts_ResultMatchesInput(self):
16+
value = [1, 2, 3, [4, 5, 6], {"foo": 8}]
17+
field = IterField()
18+
19+
prepped = field.get_prep_value(value)
20+
py = field.to_python(prepped)
21+
22+
self.assertEqual(value, py)
23+
24+
def test_ValueIsEmptyDict_ResultMatchesInput(self):
25+
value = {}
26+
field = IterField()
27+
28+
prepped = field.get_prep_value(value)
29+
py = field.to_python(prepped)
30+
31+
self.assertEqual(value, py)
32+
33+
def test_ValueIsDictWithElementsListAndDicts_ResultMatchesInput(self):
34+
value = {"foo": 1, "bar": [2, 3, 4], "boo": {"moo": 5}}
35+
field = IterField()
36+
37+
prepped = field.get_prep_value(value)
38+
py = field.to_python(prepped)
39+
40+
self.assertEqual(value, py)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import os
2+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_cookbook.test.settings")
3+
4+
from django.test import TestCase
5+
from django_cookbook.model.fields import IterField
6+
7+
8+
class IterField_ToPython(TestCase):
9+
def test_StringIsEmpty_ReturnedValueIsEmptyList(self):
10+
field = IterField()
11+
12+
self.assertEqual([], field.to_python(""))
13+
14+
def test_StringIsEmptyList_ReturnedValueIsEmptyList(self):
15+
field = IterField()
16+
17+
self.assertEqual([], field.to_python("[]"))
18+
19+
def test_StringIsListWithOneField_ReturnedValueIsListWithThatField(self):
20+
field = IterField()
21+
22+
self.assertEqual(["foo"], field.to_python('["foo"]'))
23+
24+
def test_StringIsListWithMultipleField_ReturnedValueIsListWithThoseFields(self):
25+
field = IterField()
26+
27+
self.assertEqual(["foo", "bar", "boo"], field.to_python('["foo","bar","boo"]'))
28+
29+
def test_StringIsListWithMultipleFieldOneOfWhichIsAList_ReturnedValueIsListWithThoseFields(self):
30+
field = IterField()
31+
32+
self.assertEqual(["foo", ["bar", "boo"], "moo"], field.to_python('["foo",["bar","boo"],"moo"]'))
33+
34+
def test_StringIsDictionary_ReturnedValueIsADictionaryWithTheCorrectFields(self):
35+
field = IterField()
36+
37+
self.assertEqual({"foo": "bar", "boo": ["moo", "maa"]}, field.to_python('{"foo":"bar", "boo":["moo","maa"]}'))

django_cookbook/users.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55

66
def get_authenticated_users(include=[], exclude=[]):
77
"""
8-
Gets a lit of all the authenticated user who are in the include list and not in the exclude list
8+
Gets a lit of all the authenticated user who are in the include list and not in the exclude list.
9+
For example, assuming your user profile has a "friends" property to get all authenticated friends you would use:
910
10-
:param include:
11-
:param exclude:
12-
:return:
11+
>>> get_authenticated_users(include=user.friends, exclude=[user])
12+
13+
:param include: A list of users to include, if False no include filter is applied
14+
:param exclude: A list of users to exclude
15+
16+
:return: A query set containing all authenticate uses in
1317
"""
1418
# get the ids of the authenticated sessions
1519
sessions = Session.objects.filter(expire_date__gte=datetime.now())

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
setup(
44
name='django_cookbook',
55
version='0.0.1',
6-
packages=['django_cookbook', 'django_cookbook.templatetags'],
6+
packages=['django_cookbook', 'django_cookbook.templatetags', 'django_cookbook.model'],
77
url='',
88
license='',
99
author='',

0 commit comments

Comments
 (0)