Refactoring a field and it's references in Django - sql

I have a model that is as follows:
class Car(models.Model):
make = models.CharField(max_length=128, verbose_name=_("car make"), blank=True)
I now need to refactor this so that make becomes a class of it's own.
class Car(models.Model):
make = ForeignKey(CarMake, verbose_name=_("car make"), null=True, on_delete=models.CASCADE, blank=True)
One way I thought of was changing make to legacy_make and adding a new field, _make, and then a property / getter, but it doesn't work (I understand you can't do queries this way?)
Is the best ways really to
a) Migrate old data to use new make class or
b) Change all references to take into account possible new car make if it is present

I decided to change the charfield to a foreignkey and migrate the data according to this:
https://stackoverflow.com/a/36000084/3553653

Related

Wagtail: Extend page model

I have created in the past multiple pages for Wagtail.
Example:
class PlainPage(Page):
body = StreamField(BasicStreamBlock, null=True, blank=True)
content_panels = Page.content_panels + [
StreamFieldPanel('body'),
]
Now I would like to an extend all this pages by giving them the possibility to set them to no-index.
For this reason I would like to add a boolean field to the promote_panel.
What would be the best way adding this feature to all pages I have already created?
no_index = models.BooleanField(default=False)
promote_panels = Page.promote_panels + [
FieldPanel('no_index'),
]
What would be the correct Wagtail way, to extend all my Page classes with this code?
Using Django's Class Mixins, it is possible to add fields to all existing models without too much hassle.
1. Create a Mixin
First - create a new CustomPageMixin (name this whatever you want) that extends the Page model and has the meta abstract=True set.
class CustomPageMixin(Page):
class Meta:
abstract=True
no_index = models.BooleanField(default=False)
# adding to content_panels on other pages will need to use THIS promote_panels
# e.g. promote_panels = CustomPageMixin.promote_panels + [...]
promote_panels = Page.promote_panels + [
FieldPanel('no_index'),
]
2. Update ALL existing page models
Update all your models in use to use the mixin, instead of extending the Page class, they will actually extend your mixin directly.
from ... import CustomPageMixin
class StandardPage(CustomPageMixin):
#...
class HomePage(CustomPageMixin):
#...
3. Run Migrations
Note: This will add the no_index field to ALL pages that now extend your new mixin.
./manage.py makemigrations
./manage.py migrate
Potential Issues with this approach
This may not be the best way to do this, as it is a bit indirect and hard to understand at first glance.
This does not actually change the Page model fields, so it will only be available when you access the actual specific models' instance via Page.specific
It will be a bit more tricky to use this for special Page types such as AbstractEmailForm.

How to Annotate Specific Related Field Value

I am attempting to optimize some code. I have model with many related models, and I want to annotate and filter by the value of a field of a specific type of these related models, as they are designed to be generic. I can find all instances of the type of related model I want, or all of the models related to the parent, but not the related model of the specific type related to the parent. Can anyone advise?
I initially tried
parents = parent.objects.all()
parents.annotate(field_value=Subquery(related_model.objects.get(
field__type='specific',
parent_id=OuterRef('id'),
).value)))
But get the error This queryset contains a reference to an outer query and may only be used in a subquery. When I tried
parents = parent.objects.all()
parents.annotate(field_value=Q(related_model.objects.get(
field__type='specific',
parent_id=F('id'),
).value)))
I get DoesNotExist: related_field matching query does not exist. which seems closer but still does not work.
Model structure:
class parent(models.Model):
id = models.IntegerField(null=False, primary_key=True)
class field(models.Model):
id = models.IntegerField(null=False, primary_key=True)
type = models.CharField(max_length=60)
class related_model(models.Model):
parent = models.ForeignKey(parent, on_delete=models.CASCADE, related_name='related_models')
field = models.ForeignKey(field, on_delete=models.CASCADE, related_name='fields')
Is what I want to do even possible?
Never mind I decided to do a reverse lookup, kinda like
parent_ids = related_model.objects.filter(field__type='specific', parent_id__in=list_of_parents).values_list('parent_id')
parents.objects.filter(id__in=parents_id)

How should I model a schema in which models can be related to entities of different types in Django

Schema Option 1: https://gist.github.com/guyjacks/6ec4c1b0fa41b3f666f5c6adf2dfaf89
Schema Option 2: https://gist.github.com/guyjacks/4838cd76b2f924629d2a3f2ba316a504
I guess this is really two questions:
Which schema is recommended from a relational db perspective?
Is there an idiomatic way to model either schema in Django?
Cheers!
One way to do this would be to use Generic Relations:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
And in the model you want to relate to various models:
class SomeModel(models.Model):
...
# Below the mandatory fields for generic relation
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
And in the models you want related:
from django.contrib.contenttypes.fields import GenericRelation
class SomeOtherModel(models.Model):
...
# The relation
your_field = GenericRelation(SomeModel)
If you want to be able to use reverse queries see this from the docs:
related_query_name
The relation on the related object back to this object doesn’t exist
by default. Setting related_query_name creates a relation from the
related object back to this one. This allows querying and filtering
from the related object.
You should be careful when using them as they can quickly add complexity.
Links for more info:
https://docs.djangoproject.com/en/1.11/ref/contrib/contenttypes/#generic-relations
https://simpleisbetterthancomplex.com/tutorial/2016/10/13/how-to-use-generic-relations.html

The best way to avoid generic ForeignKey in django

I have Picture model which contains different link to images. I also have People and Car models that may have one or more images. That means that certain pictures can belong to an object in Car or People model.
I try to make ForeignKey but one of the field (car_id or people_id) will be empty.
I can't create an abstract model and make ForeignKey from Picture
The last solution that I know is genericForeignKey but it seems to complex for such trivial task.
Is there are a best way to solve the problem?
I solved the problem like this:
I created another model called Album that has only id
class People(models.Model):
name = models.CharField(max_length=100)
album = models.OneToOneField(Album)
class Car(models.Model):
horse_power = models.IntegerField()
ablum = models.OneToOneField(Album)
class Picture(models.Model):
title = models.CharField(max_length=100)
album = models.ForeignKey(Album)

Django REST framework flat, read-write serializer

In Django REST framework, what is involved in creating a flat, read-write serializer representation? The docs refer to a 'flat representation' (end of the section http://django-rest-framework.org/api-guide/serializers.html#dealing-with-nested-objects) but don't offer examples or anything beyond a suggestion to use a RelatedField subclass.
For instance, how to provide a flat representation of the User and UserProfile relationship, below?
# Model
class UserProfile(models.Model):
user = models.OneToOneField(User)
favourite_number = models.IntegerField()
# Serializer
class UserProfileSerializer(serializers.ModelSerializer):
email = serialisers.EmailField(source='user.email')
class Meta:
model = UserProfile
fields = ['id', 'favourite_number', 'email',]
The above UserProfileSerializer doesn't allow writing to the email field, but I hope it expresses the intention sufficiently well. So, how should a 'flat' read-write serializer be constructed to allow a writable email attribute on the UserProfileSerializer? Is it at all possible to do this when subclassing ModelSerializer?
Thanks.
Looking at the Django REST framework (DRF) source I settled on the view that a DRF serializer is strongly tied to an accompanying Model for unserializing purposes. Field's source param make this less so for serializing purposes.
With that in mind, and viewing serializers as encapsulating validation and save behaviour (in addition to their (un)serializing behaviour) I used two serializers: one for each of the User and UserProfile models:
class UserSerializer(serializer.ModelSerializer):
class Meta:
model = User
fields = ['email',]
class UserProfileSerializer(serializer.ModelSerializer):
email = serializers.EmailField(source='user.email')
class Meta:
model = UserProfile
fields = ['id', 'favourite_number', 'email',]
The source param on the EmailField handles the serialization case adequately (e.g. when servicing GET requests). For unserializing (e.g. when serivicing PUT requests) it is necessary to do a little work in the view, combining the validation and save behaviour of the two serializers:
class UserProfileRetrieveUpdate(generics.GenericAPIView):
def get(self, request, *args, **kwargs):
# Only UserProfileSerializer is required to serialize data since
# email is populated by the 'source' param on EmailField.
serializer = UserProfileSerializer(
instance=request.user.get_profile())
return Response(serializer.data)
def put(self, request, *args, **kwargs):
# Both UserSerializer and UserProfileSerializer are required
# in order to validate and save data on their associated models.
user_profile_serializer = UserProfileSerializer(
instance=request.user.get_profile(),
data=request.DATA)
user_serializer = UserSerializer(
instance=request.user,
data=request.DATA)
if user_profile_serializer.is_valid() and user_serializer.is_valid():
user_profile_serializer.save()
user_serializer.save()
return Response(
user_profile_serializer.data, status=status.HTTP_200_OK)
# Combine errors from both serializers.
errors = dict()
errors.update(user_profile_serializer.errors)
errors.update(user_serializer.errors)
return Response(errors, status=status.HTTP_400_BAD_REQUEST)
First: better handling of nested writes is on it's way.
Second: The Serializer Relations docs say of both PrimaryKeyRelatedField and SlugRelatedField that "By default this field is read-write..." — so if your email field was unique (is it?) it might be you could use the SlugRelatedField and it would just work — I've not tried this yet (however).
Third: Instead I've used a plain Field subclass that uses the source="*" technique to accept the whole object. From there I manually pull the related field in to_native and return that — this is read-only. In order to write I've checked request.DATA in post_save and updated the related object there — This isn't automatic but it works.
So, Fourth: Looking at what you've already got, my approach (above) amounts to marking your email field as read-only and then implementing post_save to check for an email value and perform the update accordingly.
Although this does not strictly answer the question - I think it will solve your need. The issue may be more in the split of two models to represent one entity than an issue with DRF.
Since Django 1.5, you can make a custom user, if all you want is some method and extra fields but apart from that you are happy with the Django user, then all you need to do is:
class MyUser(AbstractBaseUser):
favourite_number = models.IntegerField()
and in settings: AUTH_USER_MODEL = 'myapp.myuser'
(And of course a db-migration, which could be made quite simple by using db_table option to point to your existing user table and just add the new columns there).
After that, you have the common case which DRF excels at.