Imagine having two models:
class Service(models.Model):
key_service_name = models.ForeignKey(Key_service_type, related_name='Service_key_service_names', null=False)
service_hourly_wage_rate = models.DecimalField(max_digits=5, decimal_places=2, null=True)
service_amount = models.DecimalField(max_digits=7, decimal_places=2, null=True)
class ServiceAdditionalInfo(models.Model):
service = models.ForeignKey(Service, related_name='Service_additional_info_services', null=False)
fixed_price = models.DecimalField(max_digits=8, decimal_places=2, null=True)
Amongst other info, the Service class serializer features this field description:
service_additional_info = ServiceAdditionalInfoSerializer(read_only = True, many=False, source = 'Service_additional_info_services')
In practice, one Service instance may be referenced by 0 or 1 ServiceAdditionalInfo instance.
The serializer understandably returns a list while I would definitely prefer a dictionary.
My question: Is this the recommended way of modelling the relation?
If so, is there a django-built-in mechanism to return a dict for such cases?
For the latter case I know how to work around the issue, but since I would like to use the framework in the intended way, I'm very curious if there's something I missed regarding modelling and serializers.
Replace your foreign key with a OneToOneField
A one-to-one relationship. Conceptually, this is similar to a
ForeignKey with unique=True, but the “reverse” side of the relation
will directly return a single object.
This is the preferable way of mapping a relationship when there can be exactly one or zero objects related to each other.
As for generating a single dictionary that contains data from both models, you will need to subclass the model serializer.
Related
i am using Django along with DRF, and I am using many tables and complex relationships,
using soft delete (that is only marked deleted_at and not actually deleting anything) on my models from the begging didn't hurt much, but now we are making some new changes and deleted instances are growing.
so unique db constraints started giving errors, I know and implemented two possible solutions, one is unique_together in metaclass, with deleted at and every other unique value,
the other is:
class Meta:
constraints = [UniqueConstraint(fields=["name"], condition=Q(deleted_at != True))]
yet what I want is quite different, I want to avoid repeating all this and create a flag of uniqueness like this:
something_id = models.CharField(null=False, blank=False,
max_length=256, unique_undeleted=True)
notice the unique_undeleted parameter,
i know doing this directly requires changes in the library which is obviously bad,
but i have a parent model that i use in all others,
i thought of making a customized Constructor that will check every unique value, and make the unique on condition addition, or i could use this also
class MySpecialModel(parentModel):
somethingboolean= models.BooleanField(default=True, null=False)
something_id = models.CharField(null=False, blank=False,
max_length=256)
something_unique_too = models.CharField(null=False, blank=False,
max_length=256)
unique_undeleted_fields = ["something_id", "something_unique_too""]
and iterate in the parent model and create the UniqueConstraint for each field,
yet that does not feel right!
any guidance will be appreciated.
I am designing my database, and want to know if the relationships I am implementing are the best practice or not.
The aim is that one person can own multiple places, and multiple people can even book a single place at different times. One place can have multiple bookings.
So I am using this code:
class User(models.Model):
name = models.CharField(max_length=100)
ban = models.BooleanField(default=False)
class Place(models.Model):
name = models.CharField(max_length=50)
owner = models.ForeignKey(User, on_delete=models.CASCADE, )
class Bookings(models.Model):
date = models.CharField(max_length=100)
booker = models.ForeignKey(User, on_delete=models.CASCADE)
place = models.ForeignKey(Place, on_delete=models.CASCADE)
The expected output is a secure way to implement the relationships. Currently, they are working when I tested them in the shell, with one or two places / users, but I am not sure if this is the best approach.
It is the good way of implementing it, with an n-to-n relationship.
NB: You should consider replacing date = models.CharField(max_length=100) by a DateField
If you want safety with your data you could replace on_delete=models.CASCADE by safer modes, like on_delete=models.PROTECT
Your relationships are correct, but the current Booking model allows for multiple reservations of a same place at the same time. You'd need at least a unique constraint on (place, date). Also as olinox mentionned you definitly want a DateField (or DatetimeField) for Booking.date:
class Booking(models.Model):
date = models.DateField()
booker = models.ForeignKey(User, on_delete=models.CASCADE)
place = models.ForeignKey(Place, on_delete=models.CASCADE)
class Meta:
unique_together = [("date", "place")]
Also, use the contrib.auth User model or a compatible AbstractUser - safe authentication is not that trivial so better to use tested, proven, maintained code.
I'm trying to figure out how to store prices between cities in my project so I can work with that comfortabely and admin can change those prices comfortably.
I've decided to create a through model, according to this ANSWER, which is called Ride.
But when I do makemigrations, Django returns:
va_app.City.rides: (fields.E332) Many-to-many fields with intermediate tables must not be symmetrical.
class City(models.Model):
name = models.CharField(max_length=80)
country = models.ForeignKey('Country')
_close_cities = models.ManyToManyField('City', blank=True, related_name='close_cities_set',symmetrical=True)
rides = models.ManyToManyField('self',through='Ride')
class Ride(models.Model):
price = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
Do you know how to make it work?
PS> The only thing I want is to be able to simple access the price (like City.price(City) or something else and admin to be able to change prices.
The error is pretty clear you can't have M2M relation with intermediate table and symmetrical=True, it must be symmetrical=False.
So try with:
rides = models.ManyToManyField('self', through='Ride', symmetrical=False)
However, I think something is wrong with your model structure, you have two M2M fields pointing to self? I'm not sure whats the purpose of the Rides model, but maybe this model should only have FKs to city.
I have a semi complex model relationship in my django app.
I have a Group (a business) which can have many locations
So
A group can have many providers (person) as well. The thing is, the Provider is connected to a particular group through the group location. That is to say a provider can have that location. In reality, i believe a provider can have many locations (belonging to multiple groups). The way I have this in my django so far which I don't think correct is this way:
class GroupLocations(models.Model):
address_id = models.ForeignKey(Address, on_delete= models.SET_NULL)
group_id = models.ForeignKey(Group, on_delete=models.SET_NULL)
doing_business_as = models.CharField(max_length = 255)
created_at=models.DateField(auto_now=false, auto_now_add=true)
updated_at=models.DateField(auto_now=true, auto_now_add=true)
class ProviderLocations(models.Model):
provider_id = models.ForeignKey(Provider, on_delete=models.CASCADE)
group_location_id = models.ForeignKey(GroupLocations, on_delete=models.CASCADE)
created_at=models.DateField(auto_now=false, auto_now_add=true)
updated_at=models.DateField(auto_now=true, auto_now_add=true)
My question is, does my Group (and/or Provider) model need to have some sort of relationship specified in their model definitions?
class Group(models.Model):
group_name = models.CharField(max_length=50)
group_contact= models.CharField(max_length=50)
#do I need something like the following:
providers = models.ManyToMany(Provider, through='ProviderLocations')
provider_locations = models.ManyToMany(Group, through='GroupLocations'
class Provider(models.Model):
created_at=models.DateField(auto_now=false, auto_now_add=true)
updated_at=models.DateField(auto_now=true, auto_now_add=true)
groups = models.ManyToManyField(Group, through='GroupLocations')
group_locations = models.ManyToMany(GroupLocations, through='ProviderLocations')
This is so i can get a list of groups from a provider, and the groups locations
and I can get a list of providers from a group and the providers locations.Actually more like the locations join which to which. I am still learning Django'ss relationship systems so any constructive criticism of how to make these relationships work well together would be helpful.
My question is, does my Group (and/or Provider) model need to have
some sort of relationship specified in their model definitions?
Yes a many to many relationship. And you only need to define it for one model or the other because many to many can be traversed in both direction.
Both ends of a many-to-many relationship get automatic API access to
the other end. The API works just as a “backward” one-to-many
relationship, above.
The only difference is in the attribute naming: The model that defines
the ManyToManyField uses the attribute name of that field itself,
whereas the “reverse” model uses the lowercased model name of the
original model, plus '_set' (just like reverse one-to-many
relationships).
Source: https://docs.djangoproject.com/en/dev/topics/db/queries/#many-to-many-relationships
Thus your models can be simplified.
class Group(models.Model):
group_name = models.CharField(max_length=50)
group_contact= models.CharField(max_length=50)
providers = models.ManyToMany(Provider, through='ProviderLocations')
class Provider(models.Model):
created_at=models.DateField(auto_now=false, auto_now_add=true)
updated_at=models.DateField(auto_now=true, auto_now_add=true)
I am not quite sure why you are trying to create both a GroupLocation and ProviderLocation model. I do believe they can be merged.
I'm working on a maintenance project which has a model say Business with a custom Model Manager. This custom Model Manager adds some extra filter to all the queries executing on Business models. This Business model has a ManyToMany field to self named Trainers. So far so good, the issue comes in when I try to fetch all the Trainers associated with the Business without applying those filters.
The Business model is as given below:
class Business(Basetable):
#status P=publish H=inactive D=draft N=new
name = models.CharField(max_length=120)
slug = models.SlugField(max_length=150)
logo=models.OneToOneField("BusinessLogo",null=True,on_delete=models.SET_NULL)
categories = models.ManyToManyField("BusinessCategory",related_name='allcategories',null=True)
price_type = models.CharField(max_length=2,
choices=PRICE_CHOICES,
default=YEARLY, null=True, blank=True)
achievements = models.TextField(null=True, blank=True)
years_of_experience = models.FloatField(null=True, blank=True)
trainers = models.ManyToManyField("self",related_name='btrainers',null=True, blank=True, symmetrical=False)
expense=models.IntegerField(null=True,blank=True)
objects= CityManager()
def get_trainers(self):
return self.trainers.all()
get_trainers is the function which returns all the Trainers associated with the Business, however I want the results to bypass the CityManager and use the default Manager.
Any pointers will be appreciated.
Update:
using use_for_related_fields = False does not work. I found a related bug here. Is there a work around? I know that overriding the default objects is not a good practice, however this is what I have received.
In general, it's better to avoid filtering results in the default Manager:
It's a good idea to be careful in your choice of default manager in order to avoid a situation where overriding get_queryset() results in an inability to retrieve objects you'd like to work with.
But if you can't change the default Manager for backwards-compatibility reasons, you can still explicitly create a plain Manager and get your results using that.
class Business(Basetable):
...
objects = CityManager() # Still the first listed, and default
plain_objects = models.Manager()
Now that you have a plain Manager, use it to explicitly access the desired objects:
def get_trainers(self):
return Business.plain_objects.filter(btrainers__id=self.id)