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)