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.
Related
What's the best way, in Django, to set and keep up-to-date a many-to-many field that is (for a lack of a better term) a composite of many-to-many fields from other models?
To give a concrete example, I have a Model representing a Resume.
class Resume(models.Model):
owner = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="resume",
)
description = models.TextField(default="Resume description")
skills = models.ManyToManyField(Skill, related_name="resume")
The Resume object is referenced by another model called WorkExperience:
class WorkExperience(models.Model):
...
skills = models.ManyToManyField(Skill, related_name="work")
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
null=False,
default=1,
related_name="work_experience",
)
resume = models.ForeignKey(
Resume,
on_delete=models.CASCADE,
null=False,
default=1,
related_name="work_experience",
)
Notice that there's a fair amount of redundancy here, with both Resume and WorkExperience pointing to the same owner etc. That said, both of these Models (Resume & WorkExperience) have a field called Skills. That reference another Model called Skills.
What I'd like is to have the Resume skills to point to the same skills as the ones in WorkExperience. Any suggestions on how to do this? I also have a Model called EducationExperience which also references Skills and has a foreign key relation to Resume. Is there any way to keep the skills in Resume be in sync with both the skills in WorkExperience and EducationExperience?
A straightforward option would be to implement a method called set_resume_skills which would, when called, add the skills they have to the list of skills that Resume has
Class WorkExperience(models.Model):
# Same model as above
def set_resume_skills(self):
self.resume.skills.add(self.skills)
The issue I have with this approach is that it only adds skills, it doesn't really keep them in sync. So if I remove a skill from WorkExperience it won't be removed from Resume. Another boundary condition would be that if multiple WorkExperience objects are referencing a skill and then I remove the skill from one Object how would I make sure that the reference in Resume is still intact? I.e., two work experience objects refer to the Skill "Javascript". I remove the Skill from one WorkExperience object. The Skill "Javascript" should still be referenced by the Resume because one WorkExperience object still has a reference to it.
Edit: The reason I want to do it like this is to reduce the amount of querying done on the front-end. If the only way to filter the skills are through the the "sub-models" (WorkExperience, EducationExperience), I'd need to do two queries in my front-end instead of one. Although now that I think about it, doing two queries isn't that bad.
By your design, it seems that Skills actually belong to owner i.e the AUTH_USER. By having its M2M relation in Resume & other models will definitely cause redundancy. Why don't you just create a M2M of Skills in User model?
The point about reducing queries, there are other ways to do this as well. By using select_related or prefetch_related
class User(models.Model):
skills = models.ManyToManyField(Skill, related_name="resume")
# ... Other fields here
class Resume(models.Model):
owner = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="resume",
)
description = models.TextField(default="Resume description")
# ... Other fields here
I was approaching the problem from the wrong end. I don't have to worry about keeping the Skills in check when I just do a reverse query on the Skill objects. Currently I'm filtering the Skill QuerySet in my view like this:
class SkillViewSet(viewsets.ModelViewSet):
serializer_class = SkillSerializer
def get_queryset(self):
queryset = Skill.objects.all()
email = self.request.query_params.get("email", None)
if email:
User = get_user_model()
user = User.objects.get(email=email)
query_work_experience = Q(work__owner=user)
query_education_experience = Q(education__owner=user)
queryset = queryset.filter(
query_work_experience | query_education_experience
)
return queryset
This removes the need to keep the Skills in sync with the Resume Model.
I have models defined as follow:
class Employee(models.Model):
...
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
class Project(models.Model):
...
employees = models.ManyToManyField(Employee, null=True, blank=True)
I'm trying to retrieve all the projects that have at least one employee assigned to them, but I don't know how. I tried the following things:
projects.filter(employees__gt=0)
where projects = Project.objects.all() but I don't think this is the right query, because if I do projects.filter(employees_lte=0) it returns nothing, even if I have projects with no employees assigned. How can I retrieve what I'm looking for? Could you point to a page where I can find all the lookups I can use?
Thanks!
You can try like this using isnull:
Project.objects.filter(employees__isnull=False)
Update
If you want to check specific number of employees, maybe try like this
from django.db.models import Count
Project.objects.annotate(employee_count=Count('employees')).filter(employee_count__gt=5)
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.
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 creating my own Group model; I'm not referring to the builtin Group model. I want each hroup to be a member of another group (it's parent), but there is the one "top" group that doesn't have a parent group.
The admin interface won't let me create a group without entering a parent. I get the error personnel_group.parent_id may not be NULL. My Group model looks like this:
class Group(models.Model):
name = models.CharField(max_length=50)
parent = models.ForeignKey('self', blank=True, null=True)
order = models.IntegerField()
icon = models.ImageField(upload_to='groups', blank=True, null=True)
description = models.TextField(blank=True, null=True)
How can I accomplish this?
Thanks.
I created the database before I added blank=True, null=True to the parent field definition. syncdb can't deal with that type of change, so Django wasn't picking up on my changes.
I deleted my database and let syncdb create another and it worked fine.
Django evolution would get you out of this kind of problem without dropping your complete database