Flask remove items from another table - flask

I have a db with recipes and ingredients:
class Recipe(db.Model):
id = db.Column(db.Integer, primary_key=True)
ingredient = db.relationship('Ingredient', backref='ingredientrecipe', lazy=True)
recipe_name = db.Column(db.String(100), nullable=False)
class Ingredient(db.Model):
id = db.Column(db.Integer, primary_key=True)
recipe_id = db.Column(db.Integer, db.ForeignKey('recipe.id'), nullable=False)
name = db.Column(db.String(100))
The ingredients are listed within an html table. (For clarity, I've not included the full Bootstrap html for the modal, although it's exhibiting a bit of weird behaviour despite a unique id)
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
</tr>
</thead>
<tbody>
{% for ingredient in ingredient.items %}
<tr class="ingredient">
<th scope="row"> {{ hop.id }} </th>
<td>{{ ingredient.name }}</td>
<td>
<modal html here>
<form action="{{ url_for('deleteingredient', ingredient_id=ingredient.id, recipe_id=ingredient.recipe_id) }}" method="POST">
<input class="btn btn-danger" type="submit" value="Delete">
</form>
</modal html>
{% endfor %}
</tr>
</tbody>
</table>
And here's my delete route:
#app.route('/recipe/<int:ingredient_id>/delete', methods=['POST'])
#login_required
def deleteingredient(ingredient_id):
recipe = Recipe.query.get_or_404(recipe_id)
ingredient = Ingredient.query.filter_by(ingredientrecipe=recipe)
if recipe.author != current_user:
abort(403)
db.session.delete(ingredient)
db.session.commit()
flash('Your ingredient has been deleted', 'success')
I'm not going to post the error message that comes up when I try to do this as I think it's unrelated (it's a (sqlite3.IntegrityError) NOT NULL constraint failed on a different table related to recipe) and potentially will confuse the issue.
Wanted behaviour is to go to a single recipe page, click on a button on a single row of a list of ingredients, for a modal to pop up, and a single ingredient row to be deleted from the database.
I'm pretty sure this is down to me not comprehending the logic behind the database structure and how the routes work with related tables. Any help in reducing my brain damage would be much appreciated!

By looking at your code, I can spot some issues that could break the logic:
You never use ingredient_id in your deleteingredient function
recipe_id is not defined
What is the purpose of the ingredient relationship in Recipe model? Since a recipe has several ingredients and an ingredient can have several recipes, it's a n-n relationship. Maybe you should consider looking at this

This is the code that got it working this morning.
The issue I had with the NOT NULL was due to the route being the same address as the delete recipe route, so it was doing the same thing. I thought it was a modal problem so spent some time researching Bootstrap modals, turns out I had everything right on the frontend.
Adding the ingredient line I started getting some proper debug information and it was relatively straightforward from there.
#app.route('/recipe/ingredient/<int:ingredient_id>/delete', methods=['POST'])
#login_required
def deleteingredient(ingredient_id):
ingredient = Ingredient.query.get_or_404(ingredient_id)
db.session.delete(ingredient)
db.session.commit()
flash('Your ingredient has been deleted', 'success')
return redirect(url_for('recipe', recipe_id=ingredient.recipe_id))

Related

Formset inside a ModelForm

So lets try to explain:
I have a model called inventory, which serves to handle my products and their quantity. And I have another model that I would ask to do a certain service and would need to inform how many products and their quantity would be needed to complete that same service.
My problem is, I am using a ModelForm to render my form, but with this I just can select one product in the field items(that is a foreignkey of Inventory) and can't inform how many items would be needed. So, I'd like something to my user select one or more products from my inventory and inform how many them he will need to complete de service. I read about formset and seems to be what I need, but i can't figure out how to put in my code, or inside of my actually modelForm.
inventory models:
class Inventory(models.Model):
name= models.Charfield(max_lenght=100)
description = models.TextField()
quantity = models.PositiveIntegerField()
......
service models:
class Service(models.Model):
name = models.Charfield(max_length=100)
items = models.ForeignKey(Inventory, on_delete=models.DO_NOTHING)
description = models.TextField()
date= models.DateField(default=datetime.date.today)
....
views:
class CreateServiceView(CreateView):
model = Service
form_class = ServiceModelForm
template_name = 'service/create_service.html'
success_url = reverse_lazy('service:list_service')
template:
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
<form method="post">
{%csrf_token %}
<table class="table">
{{ form }}
</table>
<tr>
<td>
<a href="service:list_service">
<button type="submit" class="btn btn-primary">Save</button>
</a>
<a href="#">
<button type="reset" class="btn btn-primary">Cancel</button>
</a>
</td>
</tr>
</form>
{% endblock %}
I need to change this field to something which I would select the item and put the quantity.
Based on how you named your variable items with a "s", I derive that you mean to use the same service for multiple inventories, right?
In that case you cannot use items = models.ForeignKey(Inventory, ...) in Service. Rather use service = models.ForeignKey(Service, ...) in Inventory.
This way every Inventory object is related to exactly one Service object. You can then access all the inventory-objects of a specific service by doing service.inventory_set.all()

Is there a way reach to grandchild count with related name in django?

I have several models like that;
class Survey(models.Model):
name = models.CharField(max_length=200, null=False)
class SurveyCollector(models.Model):
survey = models.ForeignKey(Survey, on_delete=models.CASCADE,
related_name='survey_collector_survey')
class SurveyResponse(models.Model):
collector = models.ForeignKey(SurveyCollector, on_delete=models.CASCADE,
related_name='surveyresponse_collector')
and i would like to display survey responses count in the survey_list.html. My template code seems like that;
{% for survey in surveys %}
<tr>
<th scope="row">{{ survey.name }}</th>
<td align="center">{{ survey.survey_collector_survey.count }}</td>
</tr>
{% endfor %}
I can get surveys collectors with above code. But i cannot get true result when i try this code
<td align="center">{{ survey.survey_collector_survey.surveyresponse_collector.count }}</td>
I couldnt find right approach. Could you please help me to find the best way?
It is not a good idea to that in such a way, since you here will eventually have an N+1 problem: you will make N+1 queries to the database: one query to fetch all the surveys, and N extra queries to fetch all the SurveyCollectors. If you count grandchildren, it will even be worse.
You can simply annotate your queryset, like:
from django.db.models import Count
def some_view(request):
surveys = Survey.objects.annotate(
nresponse=Count('survey_collector_survey__surveyresponse_collector')
)
# …
return render(request, 'some_template.html',{'surveys': surveys})
Now the Survey objects that arise from this queryset will have an extra attribute .nresponse that you can then use when rendering the template:
{% for survey in surveys %}
<tr>
<th scope="row">{{ survey.name }}</th>
<td align="center">{{ survey.nresponse }}</td>
</tr>
{% endfor %}
You can define a property in your Survey model.
class Survey(models.Model):
name = ............
#property
def response_count(self):
return SurveyResponse.objects.filter(collector__survey__pk = self.pk).aggregate(Count('collector'))['collector__count']
class SurveyCollector(models.Model):
survey = ..........
class SurveyResponse(models.Model):
collector = ...........
Now in your templates, you can directly use this property.
<td align="center">{{ survey.response }}</td>
Note: Take care of the double underscores (__)

Display Django model data to table in HTML

I have two Django models that record time. Model one records time during the morning and Model two records time during the evening. I want to present both of these times along with the difference between the times within an HTML table but am confused about how to do it. I am new to Django and would really appreciate some advice.
This is what I have so far:
models.py:
class Alltime(models.Model):
id= models.ForeignKey(User, on_delete = models.CASCADE)
mtime = models.DateTimeField()
etime = models.DateTimeField()
views.py:
def panel(request):
time_data = User.objects.filter(pk__gt=1) #I need all data except for the default Super User account
get_time = Alltime.objects.all()
return render(request, 'users/interface.html', {'data': time_data, "get_time": get_time})
panel.html:
<form>
{% csrf_token %}
<table>
<tr>
<th>Name</th>
<th>Morning timeE</th>
<th>Evening time</th>
<th>Difference in hours</th>
</tr>
{% for data in data %}
<tr>
<td>{{data.username}}</td>
{% endfor %}
{% if get_time %}
{% for m in get_time %}
<td>{{m.mtime}}</td>
<td>{{m.etime}}</td>
{% endfor %}
{% else %}
<td> Not available </td>
{% endif %}
</tr>
</table>
</form>
How can I get the difference between the times and place them within the HTML table?
If I understand correctly what you want to do, then you can/need to structure your data differently. An easy way is to prepare the data in your view:
def panel(request):
time_data = User.objects.filter(pk__gt=1)
time_table=[]
for user in time_data:
morning_time = Morning.objects.filter(user=user)
evening_time = Evening.objects.filter(user=user)
diff = morning_time - evening_time
time_table.append((user.name, morning_time, evening_time, diff))
return render(request, 'users/interface.html', {'data': time_table})
And in the template:
<table>
<tr>
<th>Name</th>
<th>Morning timeE</th>
<th>Evening time</th>
<th>Difference in hours</th>
</tr>
{% for line in data %}
<tr>
<td>{{line.0}}</td>
<td>{{line.1}}</td>
<td>{{line.2}}</td>
<td>{{line.3}}</td>
</tr>
{% endfor %}
</table>
You need to add the handling of not existing data in the view code.
Some remarks:
The whole thing does not really make sense to me. I guess you will need to filter for dates too. But you should get the idea from this. And why is it in a form?
You can add a property to the Alltime model that returns the difference between the morning and evening time
#property
def diff(self):
return self.etime - self.mtime
Then in your template you can use this property
{% for m in get_time %}
<td>{{m.mtime}}</td>
<td>{{m.etime}}</td>
<td>{{m.diff}}</td>
{% endfor %}

Django:The QuerySet value for an exact lookup must be limited to one result using slicing

I am working on a project where admin can assign team to manager. But it is not working and i have no idea how it will work. Because it is raising an error saying "The QuerySet value for an exact lookup must be limited to one result using slicing."
Here is my model.py
class manager(models.Model):
name = models.CharField(max_length= 500)
designation = models.CharField(max_length= 500)
user = models.ForeignKey(User,on_delete=models.CASCADE)
class Meta:
permissions = [
("edit_task", "can edit the task"),
]
here is my views.py file for the teams of manager
#login_required (login_url= 'have the url where it will go')
#permission_required('have the permission that is assigned by me')
def supervisor(request):
return render(request, 'manager/index-3.html')
def supervisor_team(request):
print(request.user.email)
email=request.user.email
obj= Create_Team.objects.filter(status='Accept',
managers=manager.objects.filter(user__email=email))
return render(request, "manager/accept_team.html", {"object": obj})
here is my template
<div class="body table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>S No.</th>
<th>COMPANY NAME</th>
<th>TEAM MEMBER</th>
<th>EMAIL</th>
</tr>
</thead>
<tbody>
{%for object in team%}
<tr>
<form id="form_id" method="POST" action = "#">
{% csrf_token %}
<th scope="row"> {{ forloop.counter }}</th>
<td>{{object.company_name}}</td>
<td>{{object.team_member}}</td>
<td>{{object.email}}</td>
<td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
I have no idea where i am wrong.
I think this is the problem:
obj= Create_Team.objects.filter(status='Accept',
managers=manager.objects.filter(user__email=email))
if you want to filter by one manager you have to use get instead of filter:
obj= Create_Team.objects.filter(status='Accept',
managers=manager.objects.get(user__email=email))
But if you want to filter by several managers you need to use __in:
obj= Create_Team.objects.filter(status='Accept',
managers__in=manager.objects.filter(user__email=email))
Also you are passing {"object": obj}) to template but in templare you are trying to iterate over team. So change it to pass team variable:
return render(request, "manager/accept_team.html", {"team": obj})

Django : Access model data in template using Class-based generic views

I'm trying to display the model data in the template.
When I don't have any filtering, it works fine!
class UserLogList(ListView):
context_object_name = 'data'
queryset = UserLog.objects.all().values('user_id__username','event_id','time')
But if I want to have some filtering, example if I want to get the details based on the user_id where user_id = 1. I can get the user id from the request object or self.request object.
So to achieve this, I did the following in my views.py
class DisplayUserActivity(ListView):
template_name = 'user_activity.html'
uid = self.request.user.id
def get_object(self):
uid = self.request.user.id
object = super(DisplayUserActivity,self).get_object()
object.data = UserLog.objects.filter(user_id = uid).values('user_id__username','event_id','time')
return object
and my template file:
{% block content %}
<h2> Logs </h2>
<table border="1" style="width:50%">
<tr>
<th> User Name </th>
<th> Description </th>
<th> Time </th>
</tr>
{% for user in data %}
<tr>
<td> {{user.user_id__username}}</td>
<td> {{user.event_id}}</td>
<td> {{user.time}} </td>
{% endfor %}
</tr>
</table>
{% endblock %}
What am I missing?
I think you need a method called get_queryset instead of get_object when extending the generic ListView.
EDIT:
Regarding your edit, theres an issue with your template
{% for user in data %}
should be
{% for user in object_list %}
But I suspect there are deeper problems here which are hard to decipher. My advice is to look through your code and the Django documentation more carefully, and possibly switch to a custom written view until you are more comfortable. :)