Multiple backref cannot access object property on One to Many relationship SQLAlchemy - flask

In the following snippet I put a template similar to my implementation. Where we have a "Grandparent" object with a OneToMany relationship with the "Parent" object and this the same relationship with the "Child" object.
class Grandparent(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True, nullable=False)
name = db.Column(db.String(50), nullable=False, unique=True)
parents = db.relationship('Parent', backref='grandparent')
class Parent(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True, nullable=False)
name = db.Column(db.String(50), nullable=False, unique=True)
grandparent_id = Column(Integer, ForeignKey('grandparent.id'))
children = db.relationship('Child', backref='parent')
class Child(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True, nullable=False)
name = db.Column(db.String(50), nullable=False, unique=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
What I want to do is get a list through the child object with information from the father and grandfather. The jinja code is as follows:
<table>
<thead>
<tr>
<th>Children name</th>
<th>Parent name</th>
<th>Grandparent name</th>
</tr>
</thead>
<tbody>
{% for child in children %}
<tr>
<td>{{ child.name }}</td>
<td>{{ child.parent.name }}</td>
<td>{{ child.parent.grandparent.name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
I get the following error:
jinja2.exceptions.UndefinedError: 'None' has no attribute 'grandparent'
It fails when performing the 2 backref, I don't know how it should be done correctly, since in the official guide it is implemented in this way.
def add(model, **kwargs):
instance = model(**kwargs)
db.session.add(instance)
db.session.commit()
def getAll(model):
data = model.query.all()
return data
I use the function above to get all the objects from the database or add a new one.
So when the endpoint is reached I do the following:
#bp.route('/example/')
def example():
children = getAll(Child)
return render_template('index.html', children=children)
Solution: all objects must have associated parent.

Related

How to count model ForeingKeys instances per row in a template table?

I want to count how many "plans" does a "client" have per table row in my template, there's a bit of the code
urls.py>>> urlpatterns = [ path('clients/', views.indexClient, name='clients'),]
models.py
class Plan (models.Model):
name = models.CharField(max_length=100, unique=True)
client = models.ForeignKey('Client', on_delete=models.RESTRICT)
gp_code = models.CharField(max_length=13, primary_key=True)
dispatch_conditions = models.TextField()
elaboration = models.CharField(max_length=100)
reviewer = models.CharField(max_length=100)
def __str__(self):
return self.product
------------------------------------------------------------------------------------
class Client (models.Model):
name = models.CharField(max_length=100, unique=True)
rif_num = models.CharField(max_length=10, primary_key=True)
def __str__(self):
return self.name
views.py
from django.db.models import Count
from .models import *
def indexClient(request):
client_list = Client.objects.all()
plans = Client.objects.annotate(num_plan = Count('plan'))
template = loader.get_template('app/clients.html')
context={'client_list':client_list, 'plans':plans}
return HttpResponse(template.render(context, request))
template clients.html
<table>
<thead>
<tr>
<th>Client</th>
<th >Plans</th>
</tr>
</thead>
<tbody>
{% for client in client_list %}
<tr>
<td>{{ client.name }}</td>
<td>{{ plans.num_plan }}</td>
</tr>
{% endfor %}
</tbody>
</table>
i tried to apply the methods said in this, this and this question but it didn't work for me or i'm applying the Count() incorrectly
want the table to display just like...
Client
Plans
Jhon
2
Mark
4
Louis
0
Vic
1
but currently the 'Plans' displays blank like this...
Client
Plans
Jhon
Mark
Louis
Vic

How to set field value based on what user click in Django ListView

I have two models Parent and Child. I would like to display both values in a ListView, whereby there is an Add Child button for each family.
Supposed that the parents are already populated, when I click Add Child, I would love that in the form of Child, the parent field are already set to the corresponding family name (please see code below).
Simple model would be:
class Family(models.Model):
family_name = models.CharField(max_length=100, default='', blank=False)
father_name = models.CharField(max_length=100, blank=False, default='')
mother_name = models.CharField(max_length=100, blank=False, default='')
def get_absolute_url(self):
return reverse('family-list')
def __str__(self):
return str(self.family_name)
class Children(models.Model):
parent = models.ForeignKey(Family, blank=False, default=None, null=True, on_delete=models.PROTECT, related_name='their_child')
child_name = models.CharField('4Jet ID', max_length=100, default='', blank=False)
birth_date = models.DateField(default=timezone.now, blank=False)
def get_absolute_url(self):
return reverse('family-list') # both return to family list view
The View using simple generic view:
class FamilyList(ListView):
model = Family
template_name = '/family_list.html'
context_object_name = 'fam_list'
# class NewParent omitted
class NewChild(CreateView):
model = Children
template_name = '/child_new.html'
context_object_name = 'child'
fields = [
'parent', 'child_name', 'birth_date'
]
and the simplified template:
<!--file: family_list.html-->
{% for fam in fam_list %}
<table>
<tr>
<th class="header"></th>
<th class="header">Family Name</th>
<th class="header">Father's First Name</th>
<th class="header">Mother's First Name</th>
<th class="header">Add Child</th>
</tr>
<tr>
<td>+</td>
<td>{{ fam.family_name }}</td>
<td>{{ fam.father_name }}</td>
<td>{{ fam.versand_datum | date:"d M, Y" }}</td>
<td>Add Child
</td>
</tr>
<tr >
<td colspan="5">
<div>
<table>
<tr>
<th class="header">Child's First Name</th>
<th class="header">Date of Birth</th>
</tr>
{% for child in fam.their_child.all %}
<tr>
<td>{{ child.child_name }}</td>
<td>{{ child.birth_date | date:"d M, Y" }}</td>
</tr>
{% endfor %}
</table>
</div>
</td>
</tr>
</table>
{% endfor %}
I've tried playing with the get_initial method in the NewChild view but by setting pdb trace within the method, the self.request.GET.getlist() gives me empty list.
Again, I just want that when I click the Add Child button in the template, the parent field in the child form will be set corresponding to the parent that I clicked.
Any idea how to do that?
All help are much appreciated
Your template is only a template to wiew a result, not to record an other one.
You must write a form and the most simple to you is to follow the initial Django tutorial.
Url : https://docs.djangoproject.com/en/2.1/intro/tutorial04/
You need :
- write a form template
- write a service to record you form result.
- and write a new template to view your record.
Take the time to do the initial tutorial, it is simple to follow

One-to-One Relationship in Flask-SQLAlchemy Only Working One Way

I have two models within a flask application, they form a one-to-one relationship, where the 'Staff' model can be associated with one 'User'. There can be 'Staff' without them being a 'User', but not a 'User' without being 'Staff'.
Unfortunately, I'm falling at the first hurdle. I'm trying to combine the models in a HTML table, templated through Jinja2. The table is supposed to list all 'Staff' and identify whether they are a 'User'. If they are a 'User' it is supposed to identify whether or not they are active.
Here are my models as they stand:
class Staff(db.Model):
__tablename__ = 'staff'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False)
job_title = db.Column(db.String(255), nullable=True)
is_user = db.Column(db.Boolean, nullable=False, default=False)
user = db.relationship('User',
backref=db.backref('staff', lazy=True))
def __repr__(self):
return f'Staff Member: {self.id}, {self.name}, {self.job_title}'
class User(db.Model, UserMixin):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, nullable=False)
image_file = db.Column(db.String(20), nullable=False,
default='default.jpg')
password = db.Column(db.String(255), nullable=False)
active = db.Column(db.Boolean, nullable=False, default=False)
staff_id = db.Column(db.Integer, db.ForeignKey('staff.id'),
nullable=False)
def __repr__(self):
return f'User({self.name}, {self.email}, {self.image_file})'
And here is an example of the relationship in Jinja2. This code segment works as expected:
{% for user in users %}
<tr>
<td>{{ user.staff.name }}</td>
<td>{{ user.staff.job_title }}</td>
<td>{{ user.email }}</td>
{% if user.active == True %}
<td>Yes</td>
{% else %}
<td>No</td>
{% endif %}
{% endfor %}
This code, however, where I'm trying to get 'User' details while looping through all 'Staff' doesn't work at all:
{% for staff in all_staff %}
<tr>
<td>{{ staff.name }}</td>
<td>{{ staff.job_title }}</td>
{% if staff.is_user == True %}
{% if staff.user.active == True %}
<td class="text-success">Yes (Active)</td>
{% elif staff.user.active == False %}
<td class="text-warning">Yes (Inactive)</td>
{% endif %}
{% elif staff.is_user == False %}
<td class="text-danger">No</td>
{% endif %}
{% endfor %}
While the relationship works one way it doesn't work in the second table example; it returns no HTML code at all unless the staff.is_user == False statement is true.
I've tried to create a new db.relationship in the 'User' class. I've also tried a db.relationship in both classes at once. I've tried the Jinja2 if statements on the same line, as well:
{% if staff.is_user == True and staff.user.active == True %}
None of these are working however, and where I'd expect the correct html to be returned based on the results of the if statements, I'm getting no results at all when referring to the 'Users' table, via the 'Staff table.
I'm sure I'm missing something simple but I can't figure it out.
Can anybody see where I'm going wrong?
It turns out the answer was hiding in the 'One-to-May Relationships' section in the Flask-SQLAlchemy docs. In order to reference a one-to-one relationship, you need to set uselist=False in the relationship. Exactly as it sounds, it loads the connection as a scalar rather than a list.
I'd also made an error in taking some information from the main SQLAlchemy docs and not correctly loading the backref in the relationship. In Flask-SQLAlchemy, it simply needs to be declared as a string. In this case backref='staff'.
This code works exactly as expected:
class Staff(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=True, nullable=False)
job_title = db.Column(db.String(80))
is_user = db.Column(db.Boolean, default=False)
user = db.relationship('User', backref='staff', lazy=True, uselist=False)
def __repr__(self):
return f'Staff Member: {self.id}, {self.name}, {self.job_title}'
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, nullable=False)
active = db.Column(db.Boolean, nullable=False, default=False)
staff_id = db.Column(db.Integer, db.ForeignKey('staff.id'), nullable=False)
def __repr__(self):
return f'User Account: {self.id}, {self.email}'

Django template display different models fields

I'm trying to list fields from few models in a table in template. The models are joined by different fields as in below snippet:
models.py:
class Client(models.Model):
clientID = models.IntegerField(primary_key=True)
firstName = models.CharField(max_length=30)
lastName = models.CharField(max_length=50)
pesel = models.CharField(max_length=11)
#property
def loan(self):
return self.loan_set.all().first()
class Loan(models.Model):
clientID = models.ForeignKey(Client, on_delete=models.CASCADE)
loanNo = models.CharField(max_length=10)
class Case(models.Model):
loanID = models.ForeignKey(Loan, on_delete=models.CASCADE)
caseID = models.CharField(max_length=30, primary_key=True)
callForPaymentDate = models.DateField(default=None)
class Phase(models.Model):
caseID = models.ForeignKey(Case, on_delete=models.CASCADE)
phaseTypeID = models.ForeignKey(PhaseType, on_delete=models.CASCADE)
courtID = models.TextField(max_length=200)
class PhaseDetail(models.Model):
caseID = models.ForeignKey(Case, on_delete=models.CASCADE)
phaseTypeID = models.ForeignKey(PhaseType, on_delete=models.CASCADE)
dismissalDate = models.DateField(default=None, blank=True, null=True)
As you see above models are connected by ForeignKey. How do I pass those models in a view (views.py below):
def index(request):
clients = Client.objects.all()
cases = Case.objects.all()
phasedetails = PhaseDetail.objects.all()
phases = Phase.objects.all()
loans = Loan.objects.all()
return render(request, 'erepertorium/index.html',
{'clients': clients, 'cases': cases, 'phasedetails': phasedetails,
'phases': phases, 'loans': loans})
So that I can display different models fields but matched by this ForeignKey?
See below html template:
<table id="case_list" class="display">
<thead>
<tr>
<td>Sygnatura wg Powoda</td>
<td>Sygnatura EPU</td>
<td>PESEL</td>
<td>Numer pożyczki</td>
<td>Imię i nazwisko</td>
</tr>
</thead>
<tbody>
<tr></tr>
{% for c in clients %}
<tr>
<td>{{ c.case.caseID }}</td>
<td>2</td>
<td>{{ c.pesel }}</td>
<td>{{ c.loan.loanNo }}</td>
<td>{{ c.firstName }} {{ c.lastName }}</td>
</tr>
{% endfor %}
</tbody>
</table>
I've read some solutions about class based views etc. but some of the solution would require rebuilding whole models thing (over 200 lines of code). So before I do that I'd like to ask Community for help.
I know two ways that you can achieve it.
First off with a #property:
class Client(models.Model):
clientID = models.IntegerField(primary_key=True)
firstName = models.CharField(max_length=30)
lastName = models.CharField(max_length=50)
#property
def loan(self):
return self.loan_set.all().first()
the template would be:
<td>{{ c.loan.loanNo }}</td>
Or
in template, you access it via the reverse relation
<td>{{ c.loan_set.all.0.loanNo }}</td>
Make sure that before the reverse relation the instance owns the relation. with if self.loan_set.all()(Model) | {% if c.loan_set.all %} (Template)

django - legacy DB and inner joins

I have the following models -
class Schmst(models.Model):
schmst_proddt = models.DateTimeField(primary_key=True)
class Meta:
managed = False
db_table = 'schmst'
class Owner(models.Model):
owner_id = models.IntegerField(primary_key=True)
owner_name = models.CharField(max_length=30, blank=True)
class Meta:
managed = False
db_table = 'owner'
class Jobmst(models.Model):
jobmst_id = models.IntegerField(primary_key=True, db_column='jobmst_id', to_column='jobmst_id')
jobmst_type = models.SmallIntegerField()
jobmst_name = models.TextField(blank=True)
class Meta:
managed = False
db_table = 'jobmst'
class Jobrun(models.Model):
jobrun_id = models.IntegerField(primary_key=True)
jobmst_id = models.ForeignKey(Jobmst, db_column='jobmst_id', to_field='jobmst_id')
jobmst_type = models.SmallIntegerField(blank=True, null=True)
jobrun_proddt = models.ForeignKey(Schmst, db_column='jobrun_proddt', to_field='schmst_proddt')
jobrun_owner = models.ForeignKey(Owner, db_column='jobrun_owner', to_field='owner_id')
class Meta:
managed = False
db_table = 'jobrun'
From there I have a view that doesn't seem to be doing any joining.
def hungjobs(request):
template_vars['jobrun'] = Jobrun.objects.db_manager('Admiral').prefetch_related('jobmst_id__owner_id').filter(jobrun_status=51, jobrun_duration__gt=1800, jobmst_type__in=[2, 6])
t = get_template('queries\HungJobs.html')
c = Context(template_vars)
return HttpResponse(t.render(c), mimetype="text/html")
The results from there I want to parse into my template below -
<table border="1">
<tr>
<td>Owner</td>
<td>Production Date</td>
<td>Job ID</td>
<td>Duration</td>
<td>Job Name</td>
</tr>
{% for jobrun in jobrun %}
<tr>
<td>{{ jobrun.owner_name }}</td>
<td>{{ jobrun.jobrun_proddt|date:"M d, Y" }}</td>
<td>{{ jobrun.jobrun_id }}</td>
<td>{{ jobrun.jobrun_duration }}</td>
<td>{{ jobrun.jobmst_name }}</td>
</tr>
{% endfor %}
</table>
Currently only the jobrun_id and jobrun_duration are loading into the template because the values are in the model I'm directly querying. The foreignkey relationships however don't seem to be working and I'm unsure why as from everything I've checked they're designed properly.
I haven't ran your code but I see a few mistakes:
For loop variable name should be changed [or you can change the template variable name] from jobrun to jobruns. It's not a good practice to reuse variable names this way.
You need to specify what field of the foreign key needs to be printed in the template. Something like:
{{ jobrun.jobrun_proddt.schmst_proddt|date:"M d, Y" }}
for the date. As it stands right now, you are returning the objects and not a specific field of the foreign key objects.