Django models many to many relations - django

I'm moving my first steps with django and I'm trying to figure out a thing.
Suppose that we have a model.py made like this where NameEffect has a many to many relation
class Name(models.Model):
nameid = models.IntegerField()
name = models.CharField(max_length=255)
class Effect(models.Model):
effectid = models.IntegerField()
effect = models.TextField()
class NameEffect(models.Model):
nameid = models.IntegerField()
effectid = models.IntegerField()
start = models.PositiveIntegerField()
strand = models.PositiveIntegerField()
and I want to create a QuerySet where every entry contains name,effect,start,strand of the researched name. Fact is that the only solution I found was using raw SQL queries but I can't understand how to do it with the django models approach

You haven't defined any relationships here at all. You should identify the fields as ForeignKeys - and also define the implicit many-to-many relationship explicitly (although you don't actually need it for this particular query, but it'll definitely come in useful).
class Name(models.Model):
nameid = models.IntegerField(primary_key=True)
name = models.CharField(max_length=255)
effects = models.ManyToManyField('Effect', through='NameEffect')
class Effect(models.Model):
effectid = models.IntegerField(primary_key=True)
effect = models.TextField()
class NameEffect(models.Model):
name = models.ForeignKey('Name', db_column='nameid')
effect = models.ForeignKey('Effect', db_column='effectid')
start = models.PositiveIntegerField()
strand = models.PositiveIntegerField()
Now you can can query NameEffect directly to get the result you want.
data = NameEffect.objects.values('name__name', 'effect__effect', 'start', 'strand')
Also note, unless you know you really need just these fields, you should avoid using values and just query NameEffect using select_related:
name_effects = NameEffect.objects.select_related('name', 'effect')
and then access the values when you need them, eg in a template:
{% for obj in name_effects %}
{{ obj.name.name }}
{{ obj.effect.effect }}
{{ obj.start }}
{{ obj.strand }}
{% endif %}

Related

DJANGO Supplement to the data list via an intermediate table

I have an intermediate models connection
Simplified:
class Person(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
etc...
class Company(models.Model):
name = models.CharField(max_length=60)
etc...
class CompanyEnrollment(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
company_position =
models.ForeignKey(CompanyPosition,on_delete=models.CASCADE)
etc...
class Meta:
unique_together = [['person', 'company']]
class CompanyPosition(models.Model):
name = models.CharField(max_length=60)
I want to create the following array:
datas = Person.objects.All(...{All elements of Person model supplemented with CompanyPosition_name}...)
There is a case where a person does not have a company_name association
Is it possible to solve this with a query?
If you have an explicit through model for your many-to-many relationship, you could use this to only get or exclude based on the entries of this table, using an Exists clause:
enrollments = CompanyEnrollment.objects.filter(person_id=OuterRef('pk'))
enrolled_persons = Person.objects.filter(Exists(enrollments))
unenrolled_persons = Person.objects.filter(~Exists(enrollments))
If you don't have an explicit intermediate table, then it's has been generated by Django directly. You should be able to use it by referring to it with Person.enrollments.through instead of CompanyEnrollment.
Since you did not detailed much the CompanyEnrollment table, I assumed you're in the second case.
This is not the best solution in my opinion, but it works for now, with little data. I think this is a slow solution for a lot of data.
views.py I will compile the two necessary dictionaries
datas = Person.objects.all().order_by('last_name', 'first_name')
companys = CompanyEnrollment.objects.all()
Use Paginator
p = Paginator(Person.objects.all(), 20)
page = request.GET.get('page')
pig = p.get_page(page)
pages = "p" * pig.paginator.num_pages
Render HTML:
context = {
"datas": datas,
"pig": pig,
"pages": pages,
"companys": companys,
}
return render(request, "XY.html", context)
HTML template:
{% for datas in pig %}
{{datas.first_name}} {{datas.last_name}}
{% for company in companys %}
{% if datas == company.person %}
{{company.company.name}} <br>
{% endif %}
{% endfor %}
{% endfor %}
not the most beautiful, but it works ... I would still accept a more elegant solution.

Django relationships between child and parent model

I am busy with a project where I have two models and I'm not sure how to do as I am fairly new to programming.
what I would like to do is have a template that
renders all the instances of first model
and then on the same template I want to have all the sales associated with each of those instances as well as the instances with different states(for example if an instance of first model is linked to 2 sales that has a state of "Confirmed" it should say 2 next to that instance name.)
class QAAgent(models.Model):
user=models.OneToOneField(User,on_delete=models.SET_NULL,null=True)
Qa_name = models.CharField(max_length=15)
def __str__(self):
return self.user.username
States = (('Pending',"Pending"),("Confirmed","Confirmed"),("Requested","Requested),("Cancelled","Cancelled"),("Not interested","Not interested"))
class Sale(models.Model):
QA = models.ForeignKey(QAAgent,on_delete=models.SET_NULL,blank=True,
null=True,related_name="sale")
State = models.CharField(choices=States,default="Pending",max_length=15)
Date_created = models.DateTimeField(auto_now_add=True)
Date_edited = models.DateTimeField(auto_now=True)
def__str__(self):
return self.client_name + " " + self.client_surname
I am not sure how to reply to answers but thanks guys that worked perfectly!
To list all your entries of the first model including it's related items you can do the following. Just be sure to pass the correct queryset to the view context.
{% for entry in qa_agent %}
{{ entry.qa_name}}
{{ entry.user.name }}
{% for sale in entry.qa_sale.all %}
{{sale.category.state }}
{% endfor %}
{% endfor %}
For your second request just create a method in your model class which filters for the desired status. I've called it get_confirmed here.
class QAAgent(models.Model):
user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True)
qa_name = models.CharField(max_length=15) # You had Qa_name before
def get_confirmed(self):
return self.qa_sale.filter(state="Pending") # just write a method like this one for each of the other statuses.
def __str__(self):
return self.user.username
And then modify your template code so it uses this method to display the confirmed ones.
{% for entry in qa_agent %}
{{ entry.qa_name}}
{{ entry.user.name }}
{{ entry.get_confirmed.count }} # <-- like this
{% endfor %}
Just a quick side note about python code styling.
For variables and function names use snake_case. for classes use CamelCase.
So for your model properties it's best to only use lower case letters and separate words using an underscore. For classes start with an uppercase letter and separate words by using an uppercase letter for the first letter of the new word. This is how it should look like:
states = (
("Pending", "Pending"),
("Confirmed", "Confirmed"),
("Requested", "Requested"),
("Cancelled", "Cancelled"),
("Not interested", "Not interested"),
)
class Sale(models.Model):
qa = models.ForeignKey(QAAgent, on_delete=models.SET_NULL, blank=True, null=True, related_name="qa_sale")
state = models.CharField(choices=States, default="Pending", max_length=15)
date_created = models.DateTimeField(auto_now_add=True)
date_edited = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.client_name} {self.client_surname}" # If you concatenate strings use an fstring like I did here. It's more readable

One QuerySet for two tables (one to many related)

I have created one to many relationship (two tables) such that every user has its own IP connections list. Every user has many connections.
My models are shown below:
class Conn(models.Model):
src_ip = models.CharField(max_length=18, unique=False,default=None,blank=True,null=True)
src_port = models.CharField(max_length=6, unique=False,default=None,blank=True,null=True)
dst_ip = models.CharField(max_length=18, unique=False,default=None,blank=True,null=True)
dst_port = models.CharField(max_length=6, unique=False,default=None,blank=True,null=True)
proto = models.CharField(max_length=6, unique=False,default=None,blank=True,null=True)
start_data = models.CharField(max_length=18, unique=False,default=None,blank=True,null=True)
r_user = models.ForeignKey(User, on_delete=models.CASCADE)
class User(models.Model):
e_user = models.CharField(max_length=15, unique=False,default=None,blank=True,null=True)
e_dev = models.CharField(max_length=15, unique=False,default=None,blank=True,null=True)
e_session = models.CharField(max_length=9, unique=False,default=None,blank=True,null=True)
e_start = models.CharField(max_length=20, unique=False,default=None,blank=True,null=True)
e_stop = models.CharField(max_length=20, unique=False,default=None,blank=True,null=True)
e_summary = models.CharField(max_length=20, unique=False,default=None,blank=True,null=True)
e_ip = models.CharField(max_length=20, unique=False,default=None,blank=True,null=True)
I'm trying to get all Users with their connections (Conn) in one QuerySet and then display everything in template. So far I can display every User without any problems with
q=Users.objects.all()
and passing the QuerySet to the template.
The question may be a bit not smart but how can I query all Users including related connections (Conn) as one QuerySet and then enumerate this connections in a form?
Use prefetch_related:
users = User.objects.all().prefetch_related('conn_set')
Now for each user you can look at its conn_set and see the Conn objects linked to it. Assuming you pass users to your template as a context variable users, something like this should work:
{% for user in users %}
{{ user.e_user }}
{% for connection in user.conn_set.all }}
{{ connection.src_ip }}
{% endfor %}
{% endfor %}
Adjust fields and add other markup to suit your needs.
From the documentation, prefetch_related
Returns a QuerySet that will automatically retrieve, in a single batch, related objects for each of the specified lookups.
If this had been a one-to-one relationship or if you'd been trying to look things up in the other direction, finding all Conn objects along with the related User, you could have used select_related, which is even more efficient.
Note that you can also clean up some of your fields by choosing more appropriate field types. For example, consider GenericIPAddressField for src_ip and dst_ip.

subquery in django

I have 2 models:
class Professors(models.Model):
professors_name = models.CharField('professor', max_length=32, unique=True)
class Discipline(models.Model):
auditorium = models.IntegerField('auditorium')
professors_name = models.ForeignKey(Professors)
In views:
disciplines = Discipline.objects.all()
So, I have number of auditorium and professors_name_id. But I need full profrssors name, not id. How to do it?
Models:
# models usually named in the singular
class Professor(models.Model):
professors_name = models.CharField('professor', max_length=32, unique=True)
class Discipline(models.Model):
auditorium = models.IntegerField('auditorium')
# your pointer is to a professor, not to the name
professor = models.ForeignKey(Professor)
In view:
# select_related('professor') to avoid a second query when accessing professor
disciplines = Discipline.objects.select_related('professor')
Template:
{% for disc in disciplines %}
{{ disc.auditorium }}: {{ disc.professor.name }}
{% endfor %}
For values:
Discipline.objects.values('auditorium', 'professor__name')
Django ORM will always returns the objects not the ids. You should have a design like this.
class Professor(models.Model):
name = models.CharField('professor', max_length=32, unique=True)
class Discipline(models.Model):
auditorium = models.IntegerField('auditorium')
professor = models.ForeignKey(Professors)
and use discipline.professor.name to retrieve the name alone.

django many to many get attributes from other object

So I've been using django for a while now, and it's great. I've recently come across a little bit of a problem, and I'm sure there's a crappy way to get it to work, but what I've found with Django is that they've usually built in all sorts of mechanisms to do things for you. So what I'm not finding is this:
Here are my models:
class LandmarkGroup(models.Model):
Name = models.CharField(max_length=150)
Description = models.CharField(max_length=300, blank=True)
IsActive = models.BooleanField(default=True)
landmarks = models.ManyToManyField('Landmark', blank=True, null=True)
def __unicode__(self):
return self.Name
class Landmark(models.Model):
Name = models.CharField(max_length=150)
Description = models.CharField(max_length=300, blank=True)
Polygon = models.PolygonField()
IsActive = models.BooleanField(default=True)
objects = models.GeoManager()
def __unicode__(self):
return self.Name
I also have another model 'Team' that has a ManyToMany with LandmarkGroup, but I'm not going to post it here. I have a view where I query for all the landmarks that have a landmarkgroup that has a team with the same team id as the one I passed in:
def mobile_startup(request):
...
landmarkGroups = LandmarkGroup.objects.filter(team=device.team, IsActive=True)
landmarks = Landmark.objects.filter(landmarkgroup__team=device.team, IsActive=True)
...
return render_to_response('webservice/mobile_startup.html', {'landmarks': landmarks, 'landmarkGroups': landmarkGroups})
Everything works, the only problem I'm having is, I'm returning this all as JSON to the mobile app, and I want to provide the landmarkGroup id for the landmark, so in my template I've been trying to:
"landmarkGroup" : {{ landmark.landmarkgroup.id }} }
but that's not working. Does anyone know any way I can get the landmarkGroup ID for each landmark in my set? Do I need to extract it when I do the query? I know I can reference each landmarkGroup in the query because I can do 'landmarkgroup__team=device.team', but I need to able to reference this object in the template
LandmarkGroup.landmarksis a ManyToManyField,therefore one Landmark can belong to multiple groups.
You should be able to output them in your template like this:
{% for group in landmark.landmarkgroup_set.all %}{{ group.pk }}{% endfor %}
The first group belonging to the landmark should be accessible through {% landmark.landmarkgroup_set.all.0 %}