I'm trying to join two tables to be able to relate the corresponding 'theme' to a 'topic'. The join seems to work, but the template gives this error when rendering:
jinja2.exceptions.UndefinedError: 'sqlalchemy.util._collections.result
object' has no attribute 'id'
How can I address Topic.id after joining the tables?
models
class Topic(db.Model):
id = db.Column(db.Integer, primary_key=True)
topic_name = db.Column(db.String(64))
opinions = db.relationship(Opinion, backref='topic')
theme_id = db.Column(db.Integer, db.ForeignKey('theme.id'))
class Theme(db.Model):
id = db.Column(db.Integer, primary_key=True)
theme_name = db.Column(db.String(64))
topics = db.relationship(Topic, backref='theme')
view
#main.route('/topics', methods=['GET', 'POST'])
def topics():
topics = db.session.query(Topic, Theme).join(Theme).order_by(Theme.theme_name).all()
themes = Theme.query
form = TopicForm()
form.theme.choices = [(t.id, t.theme_name) for t in Theme.query.order_by('theme_name')]
if form.validate_on_submit():
topic = Topic(topic_name=form.topic_name.data,
theme_id=form.theme.data)
db.session.add(topic)
return render_template('topics.html', topics=topics, themes=themes, form=form)
html jinja2 template
<table class="table table-hover parties">
<thead><tr><th>Theme</th><th>#</th><th>Name</th><th>Delete</th></tr></thead>
{% for topic in topics %}
<tr>
<td>{{ topic.theme_id }}</td>
<td>{{ topic.id }}</td>
<td>{{ topic.topic_name }}<span class="badge">0</span></td>
<td><a class="btn btn-danger btn-xs" href="{{ url_for('main.delete_topic', id=topic.id) }}" role="button">Delete</a></td>
</tr>
{% endfor %}
</table>
Alter your query to be:
topics = db.session.query(Topic).join(Theme).order_by(Theme.theme_name).all()
Using query(Topic) indicates we are interested in getting Topic values back. In contrast, your current implementation uses query(Topic, Theme) which indicates you are interested in getting tuples of (Topic, Theme).
Related
I am using slug to query the model, and render result in HTML.
The code is unable to render actual name of region, it just return None
Model
class Region(models.Model):
name = models.CharField(blank=False, unique=True)
def __str__(self):
return self.name
class Theme(models.Model):
name = models.CharField(blank=False, unique=True)
slug = models.SlugField(default="", null=False)
def __str__(self):
return self.name
class ETF(models.Model):
ticker = models.CharField(max_length=6, blank=False, db_index=True, unique=True)
full_name = models.CharField(max_length=200, blank=False)
# many to many
region = models.ManyToManyField(Region)
theme = models.ManyToManyField(Theme)
views.py
def theme_etf(request, slug): # render ETFs with theme filter
filtered_results = ETF.objects.filter(theme__slug=slug)
return render(request, "etf/list_etf.html", {
"ETFs": filtered_results
})
Part of list_etf.html
{% for ETF in ETFs %}
<tr>
<td>{{ ETF.ticker }}</td>
<td>{{ ETF.full_name }}</td>
<td>{{ ETF.region.name }}</td> # What should I use in this line
</tr>
{% endfor %}
The code is unable to render actual name of region, it just return None
Result
Ticker, Name, Region
ARKF, ARK Fintech Innovation ETF, None
ARKK, ARK Innovation ETF, None
KEJI, Global X China Innovation, None
I would like to have this:
Ticker, Name, Region
ARKF, ARK Fintech Innovation ETF, Global
ARKK, ARK Innovation ETF, Global
KEJI, Global X China Innovation, China
I have the information in the database. I have checked it in admin.
Can an ETF have multiple regions as implied by your database design? If it does not I would suggest you use ForeignKey instead.
You are accessing the region field as if it were a ForeignKey.
In your database design you need to iterate over the objects saved in the ManyToManyField using .all.
{% for ETF in ETFs %}
<tr>
<td>{{ ETF.ticker }}</td>
<td>{{ ETF.full_name }}</td>
<td>{% for region in ETF.region.all %}{{ region.name }}{%endfor%}</td>
</tr>
{% endfor %}
Because you have many-to-many relationship, you cannot simply have single values. So, you have to list values.
{% for ETF in ETFs %}
<tr>
<td>{{ ETF.ticker }}</td>
<td>{{ ETF.full_name }}</td>
<td>
<ol>
{% for region in ETF.region %}
<li>{{region.name}}</li>
{% endfor %}
</ol>
</td>
</tr>
{% endfor %}
I'm working on a library system. I am unable to get the registration number of a book/books to be returned back to library...
My intention is to click on Return which captures the book name for return processing.. With what I have, when I print(book) it returns None meaning nothing has been taken from the click
My models
class Books(models.Model):
DEPARTMENT = (
('COM', 'Computer'),
('ELX', 'Electronics'),
('CIV', 'Civil'),
('BBS', 'Business'),
('MSC', 'Miscellaneous'),
)
reg_no = models.CharField(max_length=20, blank=True)
book_name = models.CharField(max_length=200)
no_of_books = models.IntegerField()
book_detail = models.TextField(default='text')
department = models.CharField(max_length=3, choices=DEPARTMENT)
def Claimbook(self):
if self.no_of_books>1:
self.no_of_books=self.no_of_books-1
self.save()
else:
print("not enough books to Claim")
def Addbook(self):
self.no_of_books=self.no_of_books+1
self.save()
def __str__(self):
return self.book_name
class Return(models.Model):
return_date = models.DateField(default=datetime.date.today)
borrowed_item = models.ForeignKey(Issue,on_delete=models.CASCADE)
def new_issue(request):
if request.method == 'POST':
i_form = IssueForm(request.POST)
if i_form.is_valid():
name = i_form.cleaned_data['borrower_id']
book = i_form.cleaned_data['book_id']
i_form.save(commit=True)
books = Books.objects.get(book_name=book)#Get a book names as selected in the dropdown
semest = Student.objects.get(name=name).semester#Get a student with a semester as selected in the dropdown
departm = Student.objects.get(name=name).depart
Books.Claimbook(books)
return redirect('new_issue')
else:
i_form = IssueForm()
semest = None
departm = None
sem_book = Semester.objects.filter(sem=semest, depart=departm)
return render(request, 'libman/new_issue.html', {'i_form': i_form, 'sem_book': sem_book})
The return view
def return_book(request):
book = request.GET.get('book_pk')
print(book)
books = Books.objects.get(id=book)
#b_id = r_form.cleaned_data['borrower_id']
Books.Addbook(books)
Issue.objects.filter(borrower_id=1, id=book).delete()
return render(request,'libman/view_issue.html',{'issue':issue})
The template that displays the borrowed books with a link to return beside each book.
{% if issue %}
<table class="layout">
<thead>
<th>Reg No.</th>
<th>Student Name</th>
<th>Book Name</th>
<th>Issue Date</th>
<th>Action</th>
</thead>
{% for borrow in issue %}
<tr>
<td>{{ borrow.borrower_id.student_id }}</td>
<td>{{ borrow.borrower_id }}</td>
<td>{{ borrow.book_id }}</td>
<td>{{ borrow.issue_date }}</td>
<td name='book_pk'>Return </td>
</tr>
{% endfor %}
</table>
{% else %}
<p> There are no books registered. </p>
{% endif %}
Issue model
class Issue(models.Model):
borrower_id = models.ForeignKey(Student,on_delete=models.CASCADE)
book_id = models.ForeignKey(Books,on_delete=models.CASCADE)
issue_date = models.DateField(default=datetime.date.today)
def __str__(self):
return str(self.book_id)
if i understood correctly - I believe you need to pass the borrow.book_id to the return view. so the return view knows which book you want return
in your template add the variable book_pk as follows
<td name='book_pk'>Return </td>
also you need to update your urls.py file to accept the new variable something like this
urlpatterns = [
path('returnbook/<book_pk>/', return_book),
]
but the above will need to also update your view function to handle the new passed argument and fetch the object etc..
def return_book(request,book_pk):
Or
you can add a form with a submit button
<form action="{% url 'return_book' %}">
<label for="book_id">Borrowed Book_id</label>
<input type="text" id="book_id" name="book_pk" value="{{ borrow.book_id }}" disabled><br><br>
<input type="submit" value="Submit">
</form>
it should work with your current code i think
At my work everyone has to plan until they've reached their planning target. To make this easier I'm building a planning tool to plan the activities. All the activities have standard durations which are saved in the PlanningActivity model. Now I want to show a list of all the planned activities with the standard duration and also sum up the total planned time in a week. How can I use strd_duration in my added Planning's? I've tried so much, but nothing seems to work...
models.py
class PlanningActivity(models.Model):
name = models.CharField(max_length=255)
is_billable = models.BooleanField()
is_auto_realised = models.BooleanField()
strd_duration = models.PositiveIntegerField()
def __str__(self):
return self.name
class Planning(models.Model):
added_by = models.ForeignKey(
User, related_name='planning_activity', on_delete=models.DO_NOTHING
)
activity = models.ForeignKey(
PlanningActivity, on_delete=models.DO_NOTHING
)
date = models.DateField()
note = models.CharField(max_length=255)
def __str__(self):
return str(self.id)
views.py
def home(request):
planning_form = PlanningForm(request.POST)
if request.user.is_authenticated:
planning = Planning.objects.filter(added_by=request.user).order_by('-date')
contracts = UserContract.objects.filter(assigned_user=request.user)
else:
planning = ''
contract = ''
if planning_form.is_valid():
new_planning = planning_form.save(commit=False)
new_planning.added_by = request.user
new_planning.save()
return redirect('/')
else:
planning_form = PlanningForm()
return render(request, 'home.html', {'planning_form': planning_form, 'planning':planning, 'contracts':contracts})
home.html
<table class="table table-striped mt-2">
<thead class="thead-light">
<tr>
<td>Activity</td>
<td>Date</td>
<td>Note</td>
<td>Duration</td>
</tr>
</thead>
<tbody>
{% for plan in planning %}
<tr>
<td>{{ plan.activity }}</td>
<td>{{ plan.date }}</td>
<td>{{ plan.note }}</td>
<td>{{ plan.activity_id }}</td> <== HERE I WANT TO SHOW strd_duration
</tr>
{% endfor %}
</tbody>
</table>
You can access the attribute through the foreign key:
{% for plan in planning %}
<tr>
<td>{{ plan.activity }}</td>
<td>{{ plan.date }}</td>
<td>{{ plan.note }}</td>
<td>{{ plan.activity.strd_duration }}</td>
</tr>
{% endfor %}
Note that in the view, you can optimize the number of queries to the database with a .select_related(…) clause [Django-doc]:
planning = Planning.objects.filter(
added_by=request.user
).select_related('activity').order_by('-date')
I have a table in my models which the stocks are saving in it and its name is Stocks
this table is desplayed in a template and i want to put a checkbox beside each row to save the checked row in another table of the model
here ismy model.py :
class Stocks(models.Model):
user=models.ForeignKey(User, null=True)
name=models.CharField(max_length=128,verbose_name=_('stockname'))
number=models.CharField(blank=True,null=True,max_length=64,verbose_name=_('number'))
brand=models.CharField(max_length=64, validators=[
RegexValidator(regex='^[A-Z]*$',message=_(u'brand must be in Capital letter'),)]
,verbose_name=_('brand'))
comment=models.CharField(blank=True,null=True,max_length=264,verbose_name=_('comment'))
price=models.PositiveIntegerField(blank=True,null=True,verbose_name=_('price'))
date=models.DateTimeField(auto_now_add = True,verbose_name=_('date'))
confirm=models.CharField(choices=checking,max_length=12,verbose_name=_('confirmation'), default=_('pending'))
def __str__(self):
return str(self.id)
class Meta:
verbose_name=_('Stock')
verbose_name_plural=_('Stocks')
def get_absolute_url(self):
return reverse('BallbearingSite:mystocks' )
class SellerDesktop(models.Model):
seller=models.OneToOneField(User, related_name='seller', blank=True, null=True)
buyer=models.OneToOneField(User, related_name='buyer', blank=True, null=True)
stock=models.ForeignKey(Stocks, related_name='stocktoseller', blank=True, null=True)
def __str__(self):
return str(self.seller) + '-' + str(self.buyer)
class Meta:
verbose_name=_('SellerDesktop')
verbose_name_plural=_('SellerDesktop')
and the Template :
<form method="post">
{% csrf_token %}
<table id="example" class="table table-list-search table-responsive table-hover table-striped" width="100%">
{% for item in myst %}
<td><input type="checkbox" name="sendtoseller" value="{{ item.id }}"></td>
<td>{{ item.user.profile.companyname}}</td>
<td>{{ item.name }}</td>
<td>{{ item.brand }}</td>
<td>{{ item.number }}</td>
<td>{{ item.pasvand }}</td>
<td>{{ item.comment }}</td>
<td>{{ item.price }}</td>
<td>{{ item.date|timesince }}</td>
</tr>
{% endfor %}
</table>
<div style="text-align: center; margin-top:0.5cm; margin-bottom:1cm;">
<input type="submit" name="toseller" value="Submit to seller " style="color:red; width:100%;"/>
</div>
</form>
and the view :
def allstocks_view(request):
if request.method=='POST':
tosave = request.POST.getlist('sendtoseller')
stockid=Stocks.objects.filter(id=tosave)
SellerDesktop.objects.create(buyer=request.user,stock=stockid)
stocks_list=Stocks.objects.all().filter(confirm=_('approved') ).order_by('-date')
#paginating for table
page = request.GET.get('page', 1)
paginator = Paginator(stocks_list, 15)
try:
myst = paginator.page(page)
except PageNotAnInteger:
myst = paginator.page(1)
except EmptyPage:
myst = paginator.page(paginator.num_pages)
context={
'allstocks':stocks_list,
'myst':myst,
}
return render(request,'BallbearingSite/sellerstocks.html',context)
this error was showed up
TypeError at /sellerstocks/
int() argument must be a string, a bytes-like object or a number, not 'list'
when i changed the code to :
stockid=Stocks.objects.filter(id=tosave[0])
this error was showed up:
ValueError at /sellerstocks/
Cannot assign "[]": "SellerDesktop.stock" must be a "Stocks" instance.
How can i insert the selected rows into new table?
the error :
Cannot assign must be a "" instance.
was gone when i changed :
Stocks.objects.filter(id=tosave[i])
to :
Stocks.objects.get(id=tosave[i])
I have the following classes defined that essentially define a Node class. Each Node can have multiple NodeIntf's assigned to it. Each NodeIntf can have multiple NodeIntfIpaddr's assigned to it. One of those NodeIntfIpaddr's maybe assigned as the mgmt_ipaddr attribute on the Node object. And one of them maybe assigned to the mgmt_ipaddr_v6 attribute. Now in my template, I have essentially a nested table for the interfaces and I want to use a radio button selector to choose which of the ipaddrs is selected for the mgmt_ipaddr(_v6) attributes on the Node object, but I'm not quite sure how to do it. I think that, as I iterate over the ipaddr_formset, I have to check to see if that ipaddr represents the selected mgmt_ipaddr, but I'm not sure how to do that. Any help would be appreciated.
class Node(models.Model):
name = models.CharField(max_length=64, primary_key=True)
mgmt_ipaddr = models.ForeignKey('NodeIntfIpaddr', null=True, on_delete=models.SET_NULL)
mgmt_ipaddr_v6 = models.ForeignKey('NodeIntfIpaddr', null=True, on_delete=models.SET_NULL)
class NodeIntf(models.Model):
intf = models.CharField(max_length=32)
node = models.ForeignKey('Node', on_delete=models.CASCADE)
class Meta:
unique_together = ('node', 'intf')
class NodeIntfIpaddr(models.Model):
node_intf = models.ForeignKey('NodeIntf', on_delete=models.CASCADE)
ipaddr = InetAddressField()
class Meta:
unique_together = ('node_intf', 'ipaddr')
class NodeForm(ModelForm):
class Meta:
model = Node
class NodeIntfForm(ModelForm):
class Meta:
model = NodeIntf
class NodeIntfIpAddrForm(ModelForm):
class Meta:
model = NodeIntfIpaddr
NodeIntfIpaddrFormSet = modelformset_factory(NodeIntfIpaddr,
form=NodeIntfIpAddrForm, extra=0)
class BaseNodeIntfFormSet(BaseInlineFormSet):
def add_fields(self, form, index):
super(BaseNodeIntfFormSet, self).add_fields(form, index)
instance = self.get_queryset()[index]
pk_value = instance.pk
form.ipaddr_formset = NodeIntfIpaddrFormSet(
queryset=NodeIntfIpaddr.objects.filter(node_intf=pk_value),
prefix='INTF_%s' % pk_value)
NodeIntfFormSet = inlineformset_factory(Node, NodeIntf,
form=NodeIntfForm, formset=BaseNodeIntfFormSet, extra=0)
class NodeUpdateView(UpdateView):
form_class = NodeForm
model = Node
def get_context_data(self, **kwargs):
c = super(NodeUpdateView, self).get_context_data(**kwargs)
node = self.get_object()
c['action'] = reverse('node-update', kwargs={'pk': node.name})
if self.request.POST:
node_intfs = NodeIntfFormSet(self.request.POST, instance=node)
if node_intfs.is_valid():
addrs = node_intfs.save_all()
else:
node_intfs = NodeIntfFormSet(instance=node)
c['node_intfs_formset'] = node_intfs
return c
Template snippet:
<table class='node_intfs'>
<thead>
<tr class='node_intf'>
<th colspan='2'></th>
<th>Name</th>
<th></th>
</tr>
<tr class='node_intf_ipaddr'>
<th>IPv4 Mgmt<br><label><input type='radio' name='mgmt_ipaddr' value=''{{ node.mgmt_ipaddr|yesno:', checked' }}>None</label></th>
<th>IPv6 Mgmt<br><label><input type='radio' name='mgmt_ipaddr_v6' value=''{{ node.mgmt_ipaddr_v6|yesno:', checked' }}>None</label></th>
<th colspan='2'></th>
</tr>
</thead>
<tbody>
{% for node_intf_form in node_intfs_formset %}
<tr class='node_intf'>
<td colspan='2'></td>
<td>{{ node_intf_form.intf }}</td>
<td></td>
</tr>
{% if node_intf_form.ipaddr_formset %}
{% for ipaddr_form in node_intf_form.ipaddr_formset %}
<tr class='node_intf_ipaddr'>
<td>TODO</td> <---- These are what I can't figure out
<td>TODO</td> <---- These are what I can't figure out
<td></td>
<td>{{ ipaddr_form.ipaddr }}</td>
</tr>
{% endfor %}
{% endif %}
{% endfor %}
</tbody>
</table>
I was able to do what I needed by using the following in my template:
<td class='center'><input type='radio' name='mgmt_ipaddr' value='{{ ipaddr_form.instance.id }}'{% if node.mgmt_ipaddr_id == ipaddr_form.instance.id %} checked='checked'{% endif %}</td>
<td class='center'><input type='radio' name='mgmt_ipaddr_v6' value='{{ ipaddr_form.instance.id }}'{% if node.mgmt_ipaddr_v6_id == ipaddr_form.instance.id %} checked='checked'{% endif %}</td>
This compares the mgmt_ipaddr(_v6)_id from the Node object with the id of the instance tied to the individual ipaddr forms, accessible as ipaddr_form.instance.id.
Just for completeness, I was also previously missing the management_form for each of the node_intf_forms and ipaddr_forms.