Skip to content

Commit

Permalink
Merge db5c121 into 4334817
Browse files Browse the repository at this point in the history
  • Loading branch information
treyhunner committed Aug 7, 2018
2 parents 4334817 + db5c121 commit 044c082
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 102 deletions.
22 changes: 13 additions & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
language: python

python:
- 2.6
- 2.7
- 3.3
- 3.4
- 3.5
- 3.6

env:
- DJANGO=Django==1.6.7
- DJANGO=Django==1.7
- DJANGO=Django==1.11
- DJANGO=Django==2.0
- DJANGO=Django==2.1

install:
- pip install --use-mirrors $DJANGO
- pip install --use-mirrors coverage coveralls
- pip install $DJANGO
- pip install coverage coveralls

script: coverage run setup.py test

after_success: coveralls

matrix:
exclude:
- python: 2.6
env: DJANGO=Django==1.7
- python: 2.7
env: DJANGO=Django==2.0
- python: 2.7
env: DJANGO=Django==2.1
- python: 3.7
env: DJANGO=Django==1.11
21 changes: 16 additions & 5 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,19 @@
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys, os
import re
import os
import sys

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
import runtests
import relatives
import django
django.setup()

project_directory = os.path.join(os.path.basename(__file__), '..')

# -- General configuration -----------------------------------------------------

Expand All @@ -45,14 +50,20 @@
project = u'django-relatives'
copyright = u'2013, Trey Hunner'

parent_dir = os.path.dirname(os.path.dirname(__file__))

def get_version():
with open(os.path.join(parent_dir, 'relatives', '__init__.py')) as f:
return re.search(r'''__version__.*([\d.]+)''', f.read()).group(1)

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = relatives.__version__
version = get_version()
# The full version, including alpha/beta/rc tags.
release = relatives.__version__
release = version

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down Expand Up @@ -125,7 +136,7 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = []

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
Expand Down
32 changes: 23 additions & 9 deletions relatives/templatetags/relatives.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from django.template import Library
from django.core.urlresolvers import reverse, NoReverseMatch
from django.urls import reverse, NoReverseMatch
from django.utils.encoding import smart_text
from django.utils.safestring import mark_safe
from django.contrib.admin.util import lookup_field
from django.contrib.admin.utils import lookup_field
from django.core.exceptions import ObjectDoesNotExist

from ..utils import get_admin_url, GenericObjects
Expand All @@ -29,8 +29,8 @@ def contents_or_fk_link(field):
except ObjectDoesNotExist:
return contents
else:
model_field = lookup_field(field_name, obj, field.model_admin)[0]
if getattr(model_field, 'rel') and hasattr(related_obj, '_meta'):
model_field, _, _ = lookup_field(field_name, obj, field.model_admin)
if model_field.remote_field and hasattr(related_obj, '_meta'):
try:
return mark_safe('<a href="%s">%s</a>' %
(get_admin_url(related_obj), contents))
Expand All @@ -39,7 +39,7 @@ def contents_or_fk_link(field):
return contents


@register.assignment_tag
@register.simple_tag
def related_objects(obj):
"""
Return list of objects related to the given model instance
Expand All @@ -53,17 +53,31 @@ def related_objects(obj):
{% endfor %}
"""
object_list = []
related_objects = (obj._meta.get_all_related_objects() +
obj._meta.get_all_related_many_to_many_objects() +
all_related_objects = [
field
for field in obj._meta.get_fields()
if (field.one_to_many or field.one_to_one) and
field.auto_created and not field.concrete
]
all_related_m2m_objects = [
field
for field in obj._meta.get_fields(include_hidden=True)
if field.many_to_many and field.auto_created
]
related_objects = (all_related_objects +
all_related_m2m_objects +
GenericObjects(obj).get_generic_objects())
for related in related_objects:
try:
to_model = getattr(related, 'related_model', related.model)
url = reverse('admin:{0}_{1}_changelist'.format(
*related.name.split(':')))
to_model._meta.app_label,
to_model._meta.model_name
))
except NoReverseMatch:
continue
object_list.append({
'plural_name': related.model._meta.verbose_name_plural,
'plural_name': to_model._meta.verbose_name_plural,
'url': smart_text('%s?%s=%s' % (url, related.field.name, obj.pk)),
})
return object_list
16 changes: 9 additions & 7 deletions relatives/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.fields import (
GenericForeignKey, GenericRelation
)


@python_2_unicode_compatible
Expand All @@ -21,7 +23,7 @@ class Pet(models.Model):

"""Pet have an admin URL and link to Pirates"""

owner = models.ForeignKey(Pirate)
owner = models.ForeignKey(Pirate, null=True, on_delete=models.SET_NULL)


@python_2_unicode_compatible
Expand All @@ -41,7 +43,7 @@ class Sailor(models.Model):
"""Sailors have an admin URL and sometimes link to ships"""

name = models.CharField(max_length=80)
ship = models.ForeignKey(Ship, null=True)
ship = models.ForeignKey(Ship, null=True, on_delete=models.DO_NOTHING)

def __str__(self):
return self.name
Expand Down Expand Up @@ -73,7 +75,7 @@ class NotInAdmin(models.Model):

"""NotInAdmins do not have an admin URL and link to Somethings"""

fk = models.ForeignKey(Something)
fk = models.ForeignKey(Something, null=True, on_delete=models.SET_NULL)


class Book(models.Model):
Expand All @@ -87,9 +89,9 @@ class Image(models.Model):

"""Image have an admin URL and link to Book via GenericForeignKey"""

ct = models.ForeignKey(ContentType)
ct = models.ForeignKey(ContentType, on_delete=models.CASCADE)
obj_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('ct', 'obj_id')
content_object = GenericForeignKey('ct', 'obj_id')


class Journal(models.Model):
Expand All @@ -98,4 +100,4 @@ class Journal(models.Model):
also it have GenericRelation link to Images"""

name = models.CharField(max_length=10)
images = generic.GenericRelation(Image)
images = GenericRelation(Image)
33 changes: 23 additions & 10 deletions relatives/tests/tests.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
from django.test import TestCase
from django.template.loader import render_to_string
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType

Expand All @@ -14,15 +14,17 @@ class ObjectEditLinkTest(TestCase):

def test_default(self):
ship = Ship.objects.create(id=1, name="Star of India")
self.assertEqual(object_edit_link()(ship),
'<a href="/adm/tests/ship/1/">Star of India</a>')
self.assertEqual(
object_edit_link()(ship),
'<a href="/adm/tests/ship/1/change/">Star of India</a>',
)
pirate = Pirate.objects.create(id=1, name="Lowell Taylor")
self.assertEqual(object_edit_link()(pirate), "Lowell Taylor")

def test_custom_edit_text(self):
ship = Ship.objects.create(id=1, name="Star of India")
self.assertEqual(object_edit_link("Go There")(ship),
'<a href="/adm/tests/ship/1/">Go There</a>')
'<a href="/adm/tests/ship/1/change/">Go There</a>')

def test_default_blank_text(self):
pirate = Pirate.objects.create(id=1, name="Lowell Taylor")
Expand All @@ -32,7 +34,7 @@ def test_custom_edit_and_blank_text(self):
ship = Ship.objects.create(id=1, name="Star of India")
pirate = Pirate.objects.create(id=1, name="Lowell Taylor")
self.assertEqual(object_edit_link("Go There", "N/A")(ship),
'<a href="/adm/tests/ship/1/">Go There</a>')
'<a href="/adm/tests/ship/1/change/">Go There</a>')
self.assertEqual(object_edit_link("Go There", "N/A")(pirate), "N/A")


Expand All @@ -44,8 +46,10 @@ def test_no_admin_url(self):

def test_with_primary_key(self):
ship = Ship.objects.create(id=1, name="Star of India")
self.assertEqual(object_link(ship),
'<a href="/adm/tests/ship/1/">Star of India</a>')
self.assertEqual(
object_link(ship),
'<a href="/adm/tests/ship/1/change/">Star of India</a>',
)

def test_no_primary_key(self):
ship = Ship(name="Star of India")
Expand All @@ -65,7 +69,7 @@ def test_foreign_key(self):
sailor = Sailor.objects.create(name="John Ford", ship=ship)
response = self.client.get(reverse('admin:tests_sailor_change',
args=[sailor.id]))
self.assertIn(b'<a href="/adm/tests/ship/1/">Star of India</a>',
self.assertIn(b'<a href="/adm/tests/ship/1/change/">Star of India</a>',
response.content)

def test_no_foreign_key(self):
Expand All @@ -90,12 +94,21 @@ def test_nullable_foreign_key(self):
sailor = Sailor.objects.create(name="John Ford")
response = self.client.get(reverse('admin:tests_sailor_change',
args=[sailor.id]))
self.assertIn(b'(None)', response.content)
self.assertIn(b'<p>-</p>', response.content)

def test_deleted_foreign_key(self):
self.login()
ship = Ship.objects.create(id=1, name="Star of India")
sailor = Sailor.objects.create(name="John Ford", ship=ship)
ship.delete() # Sailor won't get deleted
response = self.client.get(reverse('admin:tests_sailor_change',
args=[sailor.id]))
self.assertIn(b'<p>-</p>', response.content)

def test_add_form_for_non_nullable_fk(self):
self.login()
response = self.client.get(reverse('admin:tests_pet_add'))
self.assertIn(b'(None)', response.content)
self.assertIn(b'<p>-</p>', response.content)


class RelatedObjectsTagTest(TestCase):
Expand Down
9 changes: 4 additions & 5 deletions relatives/tests/urls.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from django.conf.urls import patterns, include, url
from django.conf.urls import url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns(
'',
url(r'^adm/', include(admin.site.urls), name='admin'),
)
urlpatterns = [
url("adm/", admin.site.urls),
]
9 changes: 4 additions & 5 deletions relatives/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
from django.utils.encoding import smart_text
from .compat import format_html
from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import NoReverseMatch
from django.core.urlresolvers import reverse
from django.urls import reverse, NoReverseMatch
from django.core.cache import cache
from django.conf import settings
from django.contrib.contenttypes.generic import GenericForeignKey as GFK
from django.contrib.contenttypes.fields import GenericForeignKey as GFK


def get_admin_url(obj):
Expand Down Expand Up @@ -89,7 +88,7 @@ def __init__(self, _object):
self.cache_key = getattr(settings, 'RELATIVES_CACHE_KEY',
'relatives_cache')
self.cache_time = getattr(settings, 'RELATIVES_CACHE_TIME',
int(60*60*24))
60 * 60 * 24)
self.generic_objects = []

def get_generic_objects(self):
Expand All @@ -110,7 +109,7 @@ def _fill_generic_fields_cache(self):
if self._generic_fields_cache is None:
self._generic_fields_cache = []
for ct in ContentType.objects.all():
vf = ct.model_class()._meta.virtual_fields
vf = ct.model_class()._meta.private_fields
self._generic_fields_cache += [
x for x in vf if isinstance(x, GFK)]
cache.set(self.cache_key,
Expand Down
14 changes: 13 additions & 1 deletion runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,24 @@
'ENGINE': 'django.db.backends.sqlite3',
}
},
MIDDLEWARE_CLASSES=[
MIDDLEWARE=[
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
],
ROOT_URLCONF='relatives.tests.urls',
STATIC_URL='/static/',
TEMPLATES=[
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'APP_DIRS': True,
'OPTIONS': {
"context_processors": [
'django.contrib.auth.context_processors.auth'
]
}
},
],
)


Expand Down

0 comments on commit 044c082

Please sign in to comment.