diff --git a/CHANGES.rst b/CHANGES.rst
index 37dc2da..278ff3d 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,6 +1,18 @@
Changes
=======
+Unreleased
+----------
+
+- Use related_name (if present) when linking to reverse relations
+
+1.2.2 (2021-09-15)
+------------------
+
+- Add support for Django 3.1 and 3.2
+- Drop support for Python 3.5
+- Drop support for Django 1.11
+
1.2.0 (2020-12-15)
------------------
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index d11c391..556dd60 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -16,6 +16,17 @@ When creating a pull request, try to:
.. _CHANGES: CHANGES.rst
.. _README: README.rst
+
+Adding migrations
+-----------------
+
+When you add new models or make changes to models, you'll need to make migrations before the tests will run.
+
+To make migrations you can run::
+
+ python runtests.py makemigrations
+
+
Testing
-------
diff --git a/relatives/__init__.py b/relatives/__init__.py
index 923b987..61da74b 100644
--- a/relatives/__init__.py
+++ b/relatives/__init__.py
@@ -1 +1 @@
-__version__ = '1.2.2'
+__version__ = '1.2.2.post0'
diff --git a/relatives/templatetags/relatives.py b/relatives/templatetags/relatives.py
index e970d39..de94e0f 100644
--- a/relatives/templatetags/relatives.py
+++ b/relatives/templatetags/relatives.py
@@ -79,8 +79,12 @@ def related_objects(obj):
))
except NoReverseMatch:
continue
+ if getattr(related, 'related_name', None):
+ plural_name = related.related_name.replace("_", " ")
+ else:
+ plural_name = to_model._meta.verbose_name_plural
object_list.append({
- 'plural_name': to_model._meta.verbose_name_plural,
+ 'plural_name': plural_name,
'url': smart_text('%s?%s=%s' % (url, related.field.name, obj.pk)),
})
return object_list
diff --git a/relatives/tests/admin.py b/relatives/tests/admin.py
index 6727d4b..929a73e 100644
--- a/relatives/tests/admin.py
+++ b/relatives/tests/admin.py
@@ -46,3 +46,5 @@ class ImageAdmin(admin.ModelAdmin):
admin.site.register(models.Actor)
admin.site.register(models.Book)
admin.site.register(models.Journal)
+admin.site.register(models.Eater)
+admin.site.register(models.Meal)
diff --git a/relatives/tests/migrations/0003_auto_20210927_1618.py b/relatives/tests/migrations/0003_auto_20210927_1618.py
new file mode 100644
index 0000000..760a120
--- /dev/null
+++ b/relatives/tests/migrations/0003_auto_20210927_1618.py
@@ -0,0 +1,73 @@
+# Generated by Django 3.2.7 on 2021-09-27 16:18
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('tests', '0002_shape'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='actor',
+ name='id',
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ migrations.AlterField(
+ model_name='book',
+ name='id',
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ migrations.AlterField(
+ model_name='image',
+ name='id',
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ migrations.AlterField(
+ model_name='journal',
+ name='id',
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ migrations.AlterField(
+ model_name='movie',
+ name='id',
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ migrations.AlterField(
+ model_name='notinadmin',
+ name='id',
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ migrations.AlterField(
+ model_name='pet',
+ name='id',
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ migrations.AlterField(
+ model_name='pirate',
+ name='id',
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ migrations.AlterField(
+ model_name='sailor',
+ name='id',
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ migrations.AlterField(
+ model_name='shape',
+ name='id',
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ migrations.AlterField(
+ model_name='ship',
+ name='id',
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ migrations.AlterField(
+ model_name='something',
+ name='id',
+ field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ ]
diff --git a/relatives/tests/migrations/0004_eater_meal.py b/relatives/tests/migrations/0004_eater_meal.py
new file mode 100644
index 0000000..c24abc0
--- /dev/null
+++ b/relatives/tests/migrations/0004_eater_meal.py
@@ -0,0 +1,30 @@
+# Generated by Django 3.2.7 on 2021-09-27 16:19
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('tests', '0003_auto_20210927_1618'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Eater',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=10)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Meal',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=10)),
+ ('prepared', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meals_prepared', to='tests.eater')),
+ ('reviewed', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meals_reviewed', to='tests.eater')),
+ ],
+ ),
+ ]
diff --git a/relatives/tests/models.py b/relatives/tests/models.py
index 9ccd477..3dd9c1b 100644
--- a/relatives/tests/models.py
+++ b/relatives/tests/models.py
@@ -108,3 +108,27 @@ class Journal(models.Model):
name = models.CharField(max_length=10)
images = GenericRelation(Image)
+
+
+class Eater(models.Model):
+
+ """Eaters have an admin URL and Meal links to them in two ways."""
+
+ name = models.CharField(max_length=10)
+
+
+class Meal(models.Model):
+
+ """Meals link to Eaters twice."""
+
+ name = models.CharField(max_length=10)
+ prepared = models.ForeignKey(
+ Eater,
+ related_name="meals_prepared",
+ on_delete=models.CASCADE,
+ )
+ reviewed = models.ForeignKey(
+ Eater,
+ related_name="meals_reviewed",
+ on_delete=models.CASCADE,
+ )
diff --git a/relatives/tests/templates/related_objects_fk_test.html b/relatives/tests/templates/related_objects_fk_test.html
index f3ffc07..c6cc412 100644
--- a/relatives/tests/templates/related_objects_fk_test.html
+++ b/relatives/tests/templates/related_objects_fk_test.html
@@ -1,5 +1,6 @@
-{% load relatives %}
+{% load relatives %}{% spaceless %}
{% related_objects obj as related_objects %}
{% for related in related_objects %}
{{ related.plural_name|capfirst }}
{% endfor %}
+{% endspaceless %}
diff --git a/relatives/tests/tests.py b/relatives/tests/tests.py
index e199770..8cab8fe 100644
--- a/relatives/tests/tests.py
+++ b/relatives/tests/tests.py
@@ -8,7 +8,7 @@
from relatives.utils import object_link, object_edit_link
from .models import (Pirate, Pet, Ship, Sailor, Movie, Actor, NotInAdmin,
- Shape, Something, Book, Image, Journal)
+ Shape, Something, Book, Image, Journal, Eater)
class ObjectEditLinkTest(TestCase):
@@ -148,6 +148,16 @@ def test_foreign_keys(self):
self.assertEqual(body.strip(),
'Sailors')
+ def test_two_foreign_keys(self):
+ eater = Eater.objects.create(id=1, name="Cheryl")
+ body = render_to_string('related_objects_fk_test.html', {'obj': eater})
+ self.assertEqual(
+ body.strip(),
+ 'Meals prepared'
+ 'Meals reviewed',
+ )
+
+
def test_no_admin_url(self):
thing = Something.objects.create()
NotInAdmin.objects.create(id=1, fk=thing)