diff --git a/api/serializers.py b/api/serializers.py index e4d31c08..6567195e 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -6,9 +6,7 @@ from rest_framework.exceptions import ValidationError from django.contrib.auth.models import User from django.utils import timezone -from taggit.serializers import ( - TagListSerializerField, TaggitSerializer -) +from taggit.serializers import TagListSerializerField, TaggitSerializer from core import models @@ -203,6 +201,7 @@ class BMISerializer(CoreModelSerializer): model = models.BMI fields = ("id", "child", "bmi", "date", "notes") + class TagsSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = models.BabyBuddyTag diff --git a/api/views.py b/api/views.py index 9b24bbee..835be2d8 100644 --- a/api/views.py +++ b/api/views.py @@ -8,6 +8,7 @@ from core import models from . import serializers, filters from .mixins import TimerFieldSupportMixin + class ChildViewSet(viewsets.ModelViewSet): queryset = models.Child.objects.all() serializer_class = serializers.ChildSerializer diff --git a/core/admin.py b/core/admin.py index 04408574..7da6b5b6 100644 --- a/core/admin.py +++ b/core/admin.py @@ -178,9 +178,11 @@ class WeightAdmin(ImportExportMixin, ExportActionMixin, admin.ModelAdmin): ) resource_class = WeightImportExportResource + class BabyBuddyTaggedItemInline(admin.StackedInline): model = models.BabyBuddyTagged + @admin.register(models.BabyBuddyTag) class BabyBuddyTagAdmin(admin.ModelAdmin): inlines = [BabyBuddyTaggedItemInline] diff --git a/core/migrations/0019_babybuddytag_babybuddytagged_note_tags.py b/core/migrations/0019_babybuddytag_babybuddytagged_note_tags.py index 1086d8da..e66245ed 100644 --- a/core/migrations/0019_babybuddytag_babybuddytagged_note_tags.py +++ b/core/migrations/0019_babybuddytag_babybuddytagged_note_tags.py @@ -11,40 +11,96 @@ import taggit.managers class Migration(migrations.Migration): dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), - ('core', '0018_bmi_headcircumference_height'), + ("contenttypes", "0002_remove_content_type_name"), + ("core", "0018_bmi_headcircumference_height"), ] operations = [ migrations.CreateModel( - name='BabyBuddyTag', + name="BabyBuddyTag", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100, unique=True, verbose_name='name')), - ('slug', models.SlugField(max_length=100, unique=True, verbose_name='slug')), - ('color', models.CharField(default=core.models.random_color, max_length=32, validators=[django.core.validators.RegexValidator('^#[0-9A-F]{6}$')], verbose_name='Color')), - ('last_used', models.DateTimeField(default=django.utils.timezone.now)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(max_length=100, unique=True, verbose_name="name"), + ), + ( + "slug", + models.SlugField(max_length=100, unique=True, verbose_name="slug"), + ), + ( + "color", + models.CharField( + default=core.models.random_color, + max_length=32, + validators=[ + django.core.validators.RegexValidator("^#[0-9A-F]{6}$") + ], + verbose_name="Color", + ), + ), + ("last_used", models.DateTimeField(default=django.utils.timezone.now)), ], options={ - 'verbose_name': 'Tag', - 'verbose_name_plural': 'Tags', + "verbose_name": "Tag", + "verbose_name_plural": "Tags", }, ), migrations.CreateModel( - name='BabyBuddyTagged', + name="BabyBuddyTagged", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.IntegerField(db_index=True, verbose_name='object ID')), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_tagged_items', to='contenttypes.contenttype', verbose_name='content type')), - ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_items', to='core.babybuddytag')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "object_id", + models.IntegerField(db_index=True, verbose_name="object ID"), + ), + ( + "content_type", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="%(app_label)s_%(class)s_tagged_items", + to="contenttypes.contenttype", + verbose_name="content type", + ), + ), + ( + "tag", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="%(app_label)s_%(class)s_items", + to="core.babybuddytag", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.AddField( - model_name='note', - name='tags', - field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='core.BabyBuddyTagged', to='core.BabyBuddyTag', verbose_name='Tags'), + model_name="note", + name="tags", + field=taggit.managers.TaggableManager( + blank=True, + help_text="A comma-separated list of tags.", + through="core.BabyBuddyTagged", + to="core.BabyBuddyTag", + verbose_name="Tags", + ), ), ] diff --git a/core/widgets.py b/core/widgets.py index fde0915e..47f02e65 100644 --- a/core/widgets.py +++ b/core/widgets.py @@ -6,49 +6,44 @@ from django.forms import Widget class TagsEditor(Widget): class Media: js = ("babybuddy/js/tags_editor.js",) - - input_type = 'hidden' - template_name = 'core/widget_tag_editor.html' + input_type = "hidden" + template_name = "core/widget_tag_editor.html" @staticmethod def __unpack_tag(tag): - return {'name': tag.name, 'color': tag.color} + return {"name": tag.name, "color": tag.color} def format_value(self, value: Any) -> Optional[str]: if value is not None and not isinstance(value, str): value = [self.__unpack_tag(tag) for tag in value] return value - + def build_attrs(self, base_attrs, extra_attrs=None): attrs = super().build_attrs(base_attrs, extra_attrs) - class_string = attrs.get('class', '') + class_string = attrs.get("class", "") class_string = class_string.replace("form-control", "") - attrs['class'] = class_string + ' babybuddy-tags-editor' + attrs["class"] = class_string + " babybuddy-tags-editor" return attrs - + def get_context(self, name: str, value: Any, attrs) -> Dict[str, Any]: from . import models - most_tags = models.BabyBuddyTag.objects.order_by( - '-last_used' - ).all()[:256] + most_tags = models.BabyBuddyTag.objects.order_by("-last_used").all()[:256] result = super().get_context(name, value, attrs) - tag_names = set(x['name'] for x in (result.get('widget', {}).get('value', None) or [])) - quick_suggestion_tags = [ - t for t in most_tags - if t.name not in tag_names - ][:5] + tag_names = set( + x["name"] for x in (result.get("widget", {}).get("value", None) or []) + ) + quick_suggestion_tags = [t for t in most_tags if t.name not in tag_names][:5] - result['widget']['tag_suggestions'] = { - 'quick': [ - self.__unpack_tag(t) for t in quick_suggestion_tags + result["widget"]["tag_suggestions"] = { + "quick": [ + self.__unpack_tag(t) + for t in quick_suggestion_tags if t.name not in tag_names ], - 'most': [ - self.__unpack_tag(t) for t in most_tags - ] + "most": [self.__unpack_tag(t) for t in most_tags], } return result