Django button in Modelclass - python

I'm making an inventory app in which I could update the values of the products from my 'home.html'.
My proj name is Inventory
App name is myapp
Problem I am facing is that every time I update the value of a Stock from my homepage, it adds a new Product instead of updating the one that I want!
I am using the ModelsForm Class provided by Django.
Using Django=1.11 and Python=3.6
My project's urls.py:
from django.conf.urls import url,include
from django.contrib import admin
from myapp.views import home
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^myapp/', include('myapp.urls', namespace="myapp")),
url(r'^myapp/home/', home, name='home'),
]
My forms.py:
from django import forms
from .models import Inventory
class Operations(forms.ModelForm):
class Meta:
model = Inventory
fields = ('stocks_left',)
My app's Model.py:
from django.db import models
import uuid
class Inventory(models.Model):
"""
Model representing a the inventory.
"""
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
inventory_name = models.CharField("INVENTORY NAME" ,max_length=200, help_text="This contains the Inventory name:")
short_name = models.CharField(max_length=20, help_text="This contains an abbreviation:")
inventory_code = models.IntegerField("INVENTORY CODE" ,default = '0', help_text="This contains the Inventory code:")
price = models.IntegerField(default = '0')
stocks_left = models.IntegerField("STOCKS LEFT",default = '0')
def __str__(self):
"""
String for representing the Model object (in Admin site etc.)
"""
return '{0} ({1}) ({2})'.format(self.inventory_name,self.inventory_code,self.stocks_left)
My app's urls.py
from django.conf.urls import url
from . import views
from django.contrib.auth.views import login
app_name= 'myapp'
urlpatterns = [
url(r'^$', login, {'template_name': 'myapp/login.html'}),
]
and my views.py
from django.shortcuts import render,redirect
from django.contrib import auth
from django.contrib.auth.models import User
from django.views import generic
from django.http import HttpResponse
from myapp.models import Inventory
from .forms import Operations
def home(request):
names = Inventory.objects.all()
if request.method == "POST":
form = Operations(request.POST)
if form.is_valid():
stocks_left = form.save(commit=False)
stocks_left.save()
return redirect('myapp/home.html')
else:
form = Operations()
return render(request, 'myapp/home.html', { "names": names, "form": form})
and my home.html template:
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<title>Inventory</title>
</head>
<body>
<h1>Welcome!</h1>
<div class="container">
<table border="5" cellpadding="10" width="1000">
<thead align="center">
<tr>
<th align="center">#</th>
<th align="center">Name</th>
<th align="center">Inventory Code</th>
<th align="center">Stocks left</th>
<th align="center">Operations</th>
</tr>
<tr>
{% for x in names %}
<td align="center"> {{ x }}</td>
<td align="center"> {{ x.inventory_name }}</td>
<td align="center"> {{ x.inventory_code }}</td>
<td align="center"> {{ x.stocks_left }}</td>
<td><form method="POST">{% csrf_token %}{{form}}<button type="button" class="btn btn-primary"><span class="glyphicon glyphicon-plus"></span></button></form><br></td>
</tr>
{% endfor %}
</table>
</div>
</body>

Here is what you can do,
Create 2 fields in forms,
class Operations(forms.ModelForm):
class Meta:
model = Inventory
fields = ('stocks_left','inventory_name')
When you get the data in the view,
if request.method == "POST":
form = Operations(request.POST)
if form.is_valid():
inv_obj = form.cleaned_data.get('inventory_name')
inv_model_obj = Inventory.objects.get(inventory_name=inv_obj) #make sure to declare in the models that this inventory_name is unique
inv_model_obj.stocks_left = form.cleaned_data.get('stocks_left')
inv_model_obj.save()
return redirect('myapp/home.html')

If you do not pass instance=the_instance_you_want_to_edit to a ModelForm it will create a new instance. How else would the ModelForm know what model instance you mean to edit?
BaseModelForm.init
if instance is None:
# if we didn't get an instance, instantiate a new one
self.instance = opts.model()
object_data = {}
else:
self.instance = instance
object_data = model_to_dict(instance, opts.fields, opts.exclude)
Anyway, it is not really going to work well if you want to keep it all on one page. Either you manually type in the object_id to pass to the ModelForm later. Or you create a FormSet with each Form representing an Inventory object plus an input field for 'stocks_left'.
The former is a bit iffy because if you mistype the primary key you are going to change the stock of the wrong Inventory without noticing (or throw a DoesNotExist error down the road).
And the latter is total overkill if you only want to change a single Inventory.
You have two options and either option requires another page/template.
One page displays your overview and the other serves to update an Inventory's stock. The overview contains links to the individual update pages.
Capture the primary key of the object you want to edit from the url.
url(r'^myapp/home/([0-9]+)/$)', home, name='home-update')
The URL dispatcher will then call your home view function with the request and the captured parameter and you can instantiate the ModelForm with it.
def home(request, pk):
names = Inventory.objects.all()
if request.method == "POST":
form = Operations(request.POST, instance=Inventory.objects.get(pk=pk)
if form.is_valid():
form.save()
return redirect('myapp/home.html')
else:
form = Operations()
return render(request, 'myapp/a_very_basic_template_to_display_a_form.html', {"form": form})
Or use a class based UpdateView (for your 'update' page):
class InventoryStockUpdateView(views.generic.UpdateView):
model = Inventory
template_name = 'myapp/a_very_basic_template_to_display_a_form.html'
fields = ['stocks_left']
success_url = reverse_lazy('home')
With this in urls.py:
url(r'^myapp/home/(?P<pk>[0-9]+)/$)', InventoryStockUpdateView.as_view(), name='home-update')
To edit Inventory with the primary key 31415 you would then type '...home/31415' into your browser.
UPDATE:
Have you had a look at django's admin actions? They let you edit instances from the changelist by selecting them via a checkbox and then running an admin action on them. While that may not be quite what you want, investigating how exactly they work should teach you some tricks that would help you in your current task.
To maybe give you a few pointers on how to proceed: add <a> tags to your template, two (+/-) for every 'row' of instances you are displaying.
The href attribute of each tag of a should contain the row's instance and the element's 'job' (adding or subtracting a stock). These two values will be used in the url conf much like I described above...
As to how you properly build the href attribute; the template tag docs should help.
Maybe like so:
<tr>
{% for x in names %}
<td align="center"> {{ x }}</td>
<td align="center"> {{ x.inventory_name }}</td>
<td align="center"> {{ x.inventory_code }}</td>
<td align="center"> {{ x.stocks_left }}</td>
<td><a href={% url ??? %}>click me to subtract!</a></td>
<td><a href={% url ??? %}>click me to add!</a></td>
</tr>
{% endfor %}
Of course, you would also need a new view function to handle those urls, but those shouldn't be very difficult to figure out.

Related

Display all User profiles, in Django template, liked by the currently logged-in User (under ManytoMany field)

I built a portal, where members can see other users' profiles and can like them.
I want to show a page where the currently logged-in users can see a list of profiles only of the members they liked.
The Model has a filed 'liked', where those likes of each member profile are stored:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
company = models.CharField(max_length=500, blank = True)
city = models.CharField(max_length=100, blank = True)
website = models.URLField(max_length=500, blank = True)
liked = models.ManyToManyField(User, related_name='user_liked', blank=True)
My views.py, and here I only show all members so on my template I can loop through each member in members... Including 'member.profile' details from the Profile model.
#login_required
def all_fav_members(request):
users = User.objects.all
context = {'members':users}
return render(request, 'club/all_fav_members.html', context)
I've tried many things, both under views.py and my HTML template, but I was not able to loop through all users associated with a specific Profile under the 'liked' field where that user is equal to request.user.
I'm new to Django, hence trying multiple things. The outcome usually is I get the whole list of members, not the ones current user liked.
One of the not working examples:
{% if member.profile.liked.filter(id=request.user.id).exists()%}
My template:
{% for member in members %}
<table class="table w-100 table-hover">
<thead>
<tr id="header-paragraph-table-top">
<th>Name & Surname</th>
<th>Email</th>
<th>Company</th>
</tr>
</thead>
<tbody>
<tr id="paragraph-table">
<td>{{ member.first_name|capfirst }} {{ member.last_name|capfirst }}</td>
<td>{{ member.email }}</td>
<td>{{ member.profile.company }}</td>
</tr>
</tbody>
</table>
urls.py
path('all_fav_members/', views.all_fav_members, name='all_fav_members'),
I would probably use template tags to solve this issue.
Read this page to get to know how to register template tags: https://docs.djangoproject.com/en/4.0/howto/custom-template-tags/
Inside your_template_tags:
from django import template
register = template.Library()
#register.filter
def liked_user(user, other_user):
return user.profile.liked.filter(id=other_user.id).exists()
Inside your template you could do the following:
{% load your_template_tags %}
{% if member|liked_user:request.user %}
Although I would probably handle it in the views.py like this:
for member in context["members"]:
member.liked_by_user = member.profile.liked.filter(id=request.user.profile.id).exists()
Then you could just use this property in your template like:
{% if member.liked_by_user %}

Django overriding get_context_data() not rendering in template

I'm learning to work with CBV's and I am currently working on a simple todo list, I want to get specific user data for specific users. The way I am doing this is by overriding the get_context_data() method in my ListView class, but the data is not showing in my template. I have created multiple users and created multiple tasks, but still does not work even though I can access them through the admin page.
Here is my code:
views.py:
class TaskList(LoginRequiredMixin, ListView):
model = Task
template_name = 'app/home.html'
context_object_name = 'tasks'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['tasks'] = context['tasks'].filter(user=self.request.user)
context['count'] = context['tasks'].filter(
complete=False).count()
return context
models.py:
from django.db import models
from django.contrib.auth.models import User
class Task(models.Model):
user = models.ForeignKey(
User, on_delete=models.CASCADE, blank=True, null=True)
title = models.CharField(max_length=150)
description = models.TextField(max_length=500)
complete = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
# Order by completion
class Meta:
ordering = ['complete']
urls.py:
from django.urls import path
from .views import (
TaskList,
TaskDetail,
TaskCreate,
TaskUpdate,
TaskDelete,
UserLogin,
UserLogout
)
urlpatterns = [
path('login/', UserLogin.as_view(), name='login'),
path('logout/', UserLogout.as_view(), name='logout'),
path('', TaskList.as_view(), name='home'),
path('task-detail/<int:pk>/', TaskDetail.as_view(), name='task-detail'),
path('task-create/', TaskCreate.as_view(), name='task-create'),
path('task-update/<int:pk>/', TaskUpdate.as_view(), name='task-update'),
path('task-delete/<int:pk>/', TaskDelete.as_view(), name='task-delete')
]
home.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>To Do List</title>
</head>
<body>
{% if request.user.is_authenticated %}
<p>{{ request.user }}</p>
Logout
{% else %}
Login
{% endif %}
<hr>
Add Task
<table>
<tr>
<th>
Items
</th>
</tr>
{% for task in tasks %}
<tr>
<td>
{{ task }}
</td>
<td>
View
</td>
<td>
Edit
</td>
<td>
Delete
</td>
</tr>
{% endfor %}
</table>
</body>
</html>
I noticed that when I comment out the line: context['tasks'] = context['tasks'].filter(user=self.request.user) the data does show up in my template but not user specific. What does this mean? Also, adding other context does seem to work, but not when I try getting user specific data. I think I'm missing something small but I can't figure it out.
filter() method or any method of querysets must be applied on models not on empty dictionary keys (eg:tasks).
Try this:
Views.py
class TaskList(LoginRequiredMixin, ListView):
model = Task
template_name = 'app/home.html'
context_object_name = 'all_tasks'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['tasks'] = self.model.objects.filter(user=self.request.user)
context['count'] = self.model.objects.filter(
complete=False).count()
return context
all_tasks will give all objects of Task model as it is ListView which gives all instances, whereas tasks which is in context of get_context_data will give user specific tasks.
Then, you can run loop in template.
Another Best Approach:
If you want to only user specific data in the template app/home.html, so you can simply override the get_queryset inside TaskView in the following way:
views.py
class TaskList(LoginRequiredMixin, ListView):
model = Task
template_name = 'app/home.html'
context_object_name = 'tasks'
def get_queryset(self):
qs = super().get_queryset()
return qs.filter(user=self.request.user)
#for count you have specifiy in get_context_data
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['count'] = self.model.objects.filter(complete=False).count()
return context
Then, without changing your code in the template file, you will be able to access user specific data.
Note: It is better to use actual view name as the suffix while working with class based views, so it will be better if you name it as TaskListView instead of only TaskView.

How to pass context to a different view function?(Django)

My models.py:
class fields(models.Model):
name = models.CharField(max_length=18)
link = models.TextField()
The link contains the hyperlink of the related name.
My views.py:
def index(request):
listing = fields.objects.all()
context ={'listing':'listing'}
return render(request,'index.html',context)
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('',index,name="index"),
]
template:
{% for i in listing %}
<tr>
<td data-label="name">{{ i.name }}</td>
<td data-label="Quote"><button>{{ i.link }}</button></td>
<tr>
{% endfor %}
This redirects to the hyperlink that is displayed in the template but I want to do some automation after it redirects into this link.
Now, I want to pass the context of this function which only includes the link to the other view function so that another view function will be:
def bypass_link(request):
# get the link from above function
# execute some selenium scripts in the link
The simple template to illustrate this will be:
{% for i in listing %}
<tr>
<td data-label="name">{{ i.name }}</td>
<td data-label="Quote"><button>{{ i.link }}</button></td>
<tr>
{% endfor %}
You can pass the id of the object into the url by altering the following:
template
<td data-label="Quote">
{{ i.link }}
</td>
urls.py
from django.conf.urls import url
url(r'^bypass_link/(?P<pk>\d+)/$', views.bypass_link, name="bypass_link"),
Then in your function you need to find the same model instance and then extract the link.
def bypass_link(request, pk=None):
instance = fields.objects.get(pk=pk)
print(instance.link) # now you have your link
Now you have access to the link via instance.link
You can pass variable to different views in Django using session
My example is using Django 3.2, be sure that sessions requirement are set in settings.py. But by the default config, the following example should work.
def index(request):
listing = fields.objects.all()
# The session object must be json serializable, pay attention
listing_list = [[list.name, list.i] for list in listing]
# set the session variable
request.session['listing_list'] = listing_list
context ={'listing':listing}
return render(request,'index.html',context)
def bypass_link(request):
# get the link from above function
# execute some selenium scripts in the link
# Got the session variable
listing_list = request.session.get('listing_list')
# Do what you want with it here

Display PostgreSQL tables contents in Django

EDIT: Fixed it! in models.py I had to add this to point it to the right table as it uses the folder name as a prefix.
class Meta:
managed = False
db_table = 'tablename'
I'm looking to display the contents of a postgres table in Django. Clearly I'm doing something wrong but can not figure out why the data won't show.
Below is what I have so far, the tables headers show but there is no data or any error messages. Checked the table in pgAdmin and there is plenty of data there.
# models.py
from django.db import models
from datetime import datetime
# Create your models here.
class SensorData(models.Model):
device_name = models.TextField()
sensor_type = models.TextField()
sensor_data = models.SmallIntegerField()
sensor_date = models.DateTimeField()
def __str__(self):
return self.device_name
# views.py
from django.shortcuts import render
from django.http import HttpResponse
from django.views.generic import ListView
from .models import SensorData
# Create your views here.
class ErrorView(ListView):
model = SensorData
template_name = 'errors.html'
# urls.py
from django.urls import path
from .views import ErrorView
urlpatterns = [
path('errors/', ErrorView.as_view(), name='errors'),
path('', ErrorView.as_view(), name='errors'),
]
# errors.html
{% extends 'layout.html' %}
{%block content %}
<section class ='infoContainer'>
<div id = 'sensorInfo'>
<h2> Error Information </h2>
<table id = "deviceTable">
<tr>
<th><strong>Device </strong></th>
<th><strong>Sensor Type </strong></th>
<th><strong>Date</strong></th>
<th><strong>Information</strong></th>
</tr>
{% for error in object_list %}
<tr>
<td> {{error.device_name}} </td>
<td> {{error.sensor_type}} </td>
<td> {{error.sensor_date}} </td>
<td> {{error.sensor_data}} </td>
</tr>
{% endfor %}
</table>
</div>
</section>
{% endblock content %}

Django - Delete an specific record

I need your help please I'm trying to delete an specific record in my table that I already created, I have trying some codes but not working in my django version 1.10.4, Can someone help me?
Here's what I have:
Views.py
from django.shortcuts import render
from django.conf import settings
from .forms import regForm
from .models import registro
from registro.forms import regForm
from django.shortcuts import get_object_or_404
from django.core.urlresolvers import reverse
def test_reg(request):
form = regForm(request.POST or None)
queryset = registro.objects.all()
# query_delete = queryset.delete()
context = {
"form": form,
"queryset": queryset,
}
if form.is_valid():
instance = form.save()
return render(request, "registro.html", context)
def delete(request, id):
note = get_object_or_404(registro, pk=id).delete()
return HttpResponseRedirect(reverse('/'))
Template
<form method="POST" action="">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Registrame" />
</form>
<style>
table, th, td {
border: 1px solid black;
}
</style>
<table>
<tr>
<th>Name</th>
...
<th>Age</th>
...
<th>Delete</th>
</tr>
{% for item in queryset %}
<tr>
...
<td>{{ item.name }}</td>
...
<td>{{ item.age }}</td>
...
<td> Delete </td>
</tr>
{% endfor %}
</table>
urls.py
from django.conf.urls import include, url
from django.contrib import admin
from myselect import views
from registro import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^select/', include('myselect.urls')),
url(r'^delete/(?P<id>\d+)/$',views.delete, name='delete'),
url(r'^$', views.test_reg, name='test_reg')
]
Model.py
from __future__ import unicode_literals
from django.db import models
# Create your models here.
from django import forms
# Create your models here.
class registro(models.Model):
name = models.CharField(max_length=100)
age = models.CharField(max_length=100)
def __unicode__(self):
return self.name
def __str__(self):
return self.name
Basically I have some users in my Database where I'm showing this through a table in my template, I want to delete the first one for example by clicking in my delete option where is in my last cell of my table
I tried this code and I got:
Reverse for 'delete' with arguments '()' and keyword arguments '{u'pk': ''}' not found. 1 pattern(s) tried: ['delete/(?P<id>\\d+)/$']
How can I solve this and make it work good?
Help please... thanks !
The problem is in your template change:
Delete
To:
Delete

Categories