Skip to content

Commit

Permalink
initial commit with basic structure for getting filter specs
Browse files Browse the repository at this point in the history
  • Loading branch information
miki725 committed Aug 24, 2015
0 parents commit 3af0a67
Show file tree
Hide file tree
Showing 22 changed files with 630 additions and 0 deletions.
21 changes: 21 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# http://editorconfig.org

root = true

[*]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
charset = utf-8
end_of_line = lf

[*.bat]
indent_style = tab
end_of_line = crlf

[LICENSE]
insert_final_newline = false

[Makefile]
indent_style = tab
19 changes: 19 additions & 0 deletions .importanizerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"groups": [
{
"type": "stdlib"
},
{
"type": "remainder"
},
{
"type": "packages",
"packages": [
"url_filter"
]
},
{
"type": "local"
}
]
}
11 changes: 11 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env python
import os
import sys


if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings")

from django.core.management import execute_from_command_line

execute_from_command_line(sys.argv)
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
DJANGO_SETTINGS_MODULE=test_project.settings
Empty file added test_project/__init__.py
Empty file.
Empty file.
53 changes: 53 additions & 0 deletions test_project/many_to_many/initial_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[
{
"pk": 1,
"fields": {
"title": "The Python Journal"
},
"model": "many_to_many.publication"
},
{
"pk": 2,
"fields": {
"title": "Science News"
},
"model": "many_to_many.publication"
},
{
"pk": 3,
"fields": {
"title": "Science Weekly"
},
"model": "many_to_many.publication"
},
{
"pk": 4,
"fields": {
"title": "Highlights for Children"
},
"model": "many_to_many.publication"
},
{
"pk": 1,
"fields": {
"headline": "Django lets you build Web apps easily",
"publications": [
1
]
},
"model": "many_to_many.article"
},
{
"pk": 2,
"fields": {
"headline": "NASA uses Python",
"publications": [
4,
2,
3,
1
]
},
"model": "many_to_many.article"
}
]
28 changes: 28 additions & 0 deletions test_project/many_to_many/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals

import six
from django.db import models


@six.python_2_unicode_compatible
class Publication(models.Model):
title = models.CharField(max_length=30)

def __str__(self):
return self.title

class Meta:
ordering = ('title',)


@six.python_2_unicode_compatible
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)

def __str__(self):
return self.headline

class Meta:
ordering = ('headline',)
Empty file.
47 changes: 47 additions & 0 deletions test_project/many_to_one/initial_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[
{
"model": "many_to_one.reporter",
"pk": 1,
"fields": {
"email": "john@example.com",
"first_name": "John",
"last_name": "Smith"
}
},
{
"model": "many_to_one.reporter",
"pk": 2,
"fields": {
"email": "paul@example.com",
"first_name": "Paul",
"last_name": "Jones"
}
},
{
"model": "many_to_one.article",
"pk": 1,
"fields": {
"reporter": 1,
"headline": "This is a test",
"pub_date": "2005-07-27"
}
},
{
"model": "many_to_one.article",
"pk": 2,
"fields": {
"reporter": 1,
"headline": "John's second story",
"pub_date": "2005-07-29"
}
},
{
"model": "many_to_one.article",
"pk": 3,
"fields": {
"reporter": 2,
"headline": "Paul's story",
"pub_date": "2006-01-17"
}
}
]
28 changes: 28 additions & 0 deletions test_project/many_to_one/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals

import six
from django.db import models


@six.python_2_unicode_compatible
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()

def __str__(self):
return "%s %s" % (self.first_name, self.last_name)


@six.python_2_unicode_compatible
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter)

def __str__(self):
return self.headline

class Meta:
ordering = ('headline',)
Empty file.
34 changes: 34 additions & 0 deletions test_project/one_to_one/initial_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[
{
"model": "one_to_one.place",
"fields": {
"name": "Demon Dogs",
"address": "944 W. Fullerton"
},
"pk": 1
},
{
"model": "one_to_one.place",
"fields": {
"name": "Ace Hardware",
"address": "1013 N. Ashland"
},
"pk": 2
},
{
"model": "one_to_one.restaurant",
"fields": {
"serves_hot_dogs": true,
"serves_pizza": false
},
"pk": 1
},
{
"model": "one_to_one.waiter",
"fields": {
"restaurant": 1,
"name": "Joe"
},
"pk": 1
}
]
33 changes: 33 additions & 0 deletions test_project/one_to_one/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals

import six
from django.db import models


@six.python_2_unicode_compatible
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)

def __str__(self):
return "%s the place" % self.name


@six.python_2_unicode_compatible
class Restaurant(models.Model):
place = models.OneToOneField(Place, primary_key=True)
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)

def __str__(self):
return "%s the restaurant" % self.place.name


@six.python_2_unicode_compatible
class Waiter(models.Model):
restaurant = models.ForeignKey(Restaurant)
name = models.CharField(max_length=50)

def __str__(self):
return "%s the waiter at %s" % (self.name, self.restaurant)
23 changes: 23 additions & 0 deletions test_project/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Bare ``settings.py`` for running tests for url_filter

DEBUG = True

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'url_filter.sqlite'
}
}

INSTALLED_APPS = (
'test_project.many_to_many',
'test_project.many_to_one',
'test_project.one_to_one',
'url_filter',
'django_extensions',
)

STATIC_URL = '/static/'
SECRET_KEY = 'foo'

MIDDLEWARE_CLASSES = []
2 changes: 2 additions & 0 deletions url_filter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals
6 changes: 6 additions & 0 deletions url_filter/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals


class SkipField(Exception):
pass
68 changes: 68 additions & 0 deletions url_filter/filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals

from django.core.exceptions import ValidationError
from django.db.models.sql.constants import QUERY_TERMS

from .utils import FilterSpec


class Filter(object):
default_lookup = 'exact'

def __init__(self, source=None, *args, **kwargs):
self._source = source
self.parent = None
self.name = None
self.init(*args, **kwargs)

def init(self, form_field, lookups=None, default_lookup=None):
self.form_field = form_field
self.lookups = lookups or list(QUERY_TERMS)
self.default_lookup = default_lookup or self.default_lookup

@property
def source(self):
return self._source or self.name

@property
def components(self):
if self.parent is None:
return []
return self.parent.components + [self.source]

def bind(self, name, parent):
self.name = name
self.parent = parent

@property
def root(self):
if self.parent is None:
return self
return self.parent.root

def get_spec(self, data):
# lookup was explicitly provided
if isinstance(data.data, dict):
if not data.is_key_value():
raise ValidationError(
'Invalid filtering data provided. '
'Data is more complex then expected. '
'Most likely additional lookup was specified '
'after the final lookup (e.g. field__in__equal=value).'
)

lookup = data.name
value = data.value.data

# use default lookup
else:
lookup = self.default_lookup
value = data.data

if lookup not in self.lookups:
raise ValidationError('"{}" lookup is not supported'.format(lookup))

is_negated = '!' in data.key

return FilterSpec(self.components, lookup, value, is_negated)
Loading

0 comments on commit 3af0a67

Please sign in to comment.