I have two models many to many relationships, I am trying to update a field by subtraction two values from the two models and save the changes to the db.
class LeaveBalance(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE,primary_key=True,)
Leave_current_balance= models.FloatField(null=True, blank=True, default=None)
Year=models.CharField(max_length=100,default='')
def __unicode__(self):
return self.Year
class NewLeave(models.Model):
user=models.ForeignKey(User,default='',on_delete=models.CASCADE)
leave_balance=models.ManyToManyField(Leave_Balance)
leave=(
('annual','annual'),
('sick','sick'),
)
Leave_type=models.CharField(max_length=100,choices=leave,blank=False,default='')
Total_working_days=models.FloatField(null=True, blank=False)
DirAuth=(
('Pending','Pending'),
('Approved','Approved'),
('Rejected','Rejected'),
)
Director_Authorization_Status=models.CharField(max_length=100,choices=DirAuth,default='Pending',blank=False)
Date_Authorized=models.DateField(null=True,blank=False)
Authorized_by_Director=models.CharField(max_length=100,default='',blank=False)
def __unicode__(self):
return self.Leave_type
here is my form, when a leave is submitted the director is notified by email. the director can login to the system to approve the leave using the form. once the leave is approved, I want to adjust the Leave_current_balance.
class DirectorForm(forms.ModelForm):
class Meta:
model=NewLeave
fields=('Director_Authorization_Status','Authorized_by_Director','Date_Authorized',)
widgets={
'Date_Authorized':DateInput()
}
This is the function that allows the director to approve the leave which throws the error: u'Leave_current_balance'
def unitDirectorForm(request,id):
if request.method=='POST':
getstaffid=NewLeave.objects.get(id=id)
form = DirectorForm(request.POST, instance=getstaffid)
if form.is_valid():
getstaffid = form.save(commit=False)
getstaffid.save()
total_days = getstaffid.Total_working_days
current_balance = getstaffid.user.leave_balance.Leave_current_balance
diff_balance = current_balance - total_days
current_balance = diff_balance
current_balance=form.fields['Leave_current_balance']
current_balance.save()
getstaffid.leave_balance.add(current_balance)
return HttpResponse('You have successfuly Authorise the leave')
else:
#getstaffid=NewLeave.objects.get(id=id)
form=DirectorForm()
#c_balance=Leave_Balance.objects.get()
balance_form = leavebbalanceForm()
return render(request,'managerauthorisedform.html',{'form':form})
You could get this working in another way too. For example:
def on_balance(user_id):
id = user_id
c_balance = LeaveBalance.objects.get(user=id)
current_balance = c_balance.Leave_current_balance
t_days = NewLeave.objects.get(user=id)
total_days = t_days.Total_working_days
current_balance = current_balance - total_days
balance = LeaveBalance.objects.get(user=id)
balance.leave_balance = current_balance
balance.save()
And the above does not cause combined expression error.
Or just a bit simpler:
def on_balance(user_id):
id = user_id
c_balance = LeaveBalance.objects.get(user=id)
current_balance = c_balance.Leave_current_balance
t_days = NewLeave.objects.get(user=id)
total_days = t_days.Total_working_days
current_balance = current_balance - total_days
c_balance.leave_balance = current_balance
c_balance.save()
UPDATE - restructuring the models and views
So the above code works if it's used in an appropriate model/form/view structure, but instead I would suggest you to restructure the whole thing starting from your models. I give you a simple working example (I tested this and works):
My app name is in this example: Myusers1 , so where ever you see that, you can change that name to your app name if needed.
So the Models:
from django.db import models
from django.conf import settings
from django.utils.text import slugify
from django.db.models import F
from django.urls import reverse
class Director(models.Model):
name = models.CharField(max_length = 100, default = '', null = True, verbose_name = 'Name of Director')
def __str__(self):
return self.name
class Staff(models.Model):
TYPE_CHOICES = (
('REGULAR', 'Regular'),
('MANAGER', 'Manager'),
('FRESH', 'Fresh'),
)
name = models.CharField(max_length = 100, default = '', null = True, unique=True, verbose_name = 'Name of staff member')
birthdate = models.DateField(blank = True, verbose_name = 'Birth date')
department = models.CharField(max_length = 100, default = '', null = True, verbose_name = 'Department')
# department could also be a choice field from another table
type = models.CharField(max_length = 20, choices = TYPE_CHOICES, verbose_name = 'Position Type', null = True)
def __str__(self):
return self.name
class LeaveBalance(models.Model):
staff = models.ForeignKey(Staff, to_field='name', on_delete = models.CASCADE, primary_key = False)
Leave_current_balance = models.FloatField(null = True, blank = True, default = '')
date_updated = models.DateTimeField(auto_now_add = True, verbose_name = 'Last Updated date and time')
def __unicode__(self):
return self.Leave_current_balance
class NewLeave(models.Model):
all_staff = Staff.objects.values()
STAFF_CHOICES = [(d['name'], d['name']) for d in all_staff]
staff = models.CharField(max_length = 100, choices = STAFF_CHOICES)
leave_days_to_approve_now = models.FloatField(null = True, blank = False, default = 5.0, verbose_name = 'Leave days for approval now')
LEAVE_CHOICES=(
('annual','annual'),
('sick','sick'),
)
Leave_type = models.CharField(max_length = 100, choices = LEAVE_CHOICES, blank = False, default = '', verbose_name = 'Type of leave')
Total_working_days = models.FloatField(null = True, blank = False, default = 200.0)
APPROVAL_STATUS_CHOICES=(
('Pending','Pending'),
('Approved','Approved'),
('Rejected','Rejected'),
)
Director_Authorization_Status = models.CharField(max_length = 100, choices = APPROVAL_STATUS_CHOICES, default = 'Pending', blank = False)
Date_Authorized = models.DateTimeField(auto_now_add = True, verbose_name = 'date and time of Authorization')
all_directors = Director.objects.values()
DIRECTOR_CHOICES = [(d['name'], d['name']) for d in all_directors]
Authorized_by_Director = models.CharField(max_length = 100, choices = DIRECTOR_CHOICES, default = '', blank = False)
def __unicode__(self):
return self.Leave_type
def get_absolute_url(self):
pass
# return reverse('newleave-detail', kwargs={'pk': self.pk}) # this should be worked out too
def save(self, *args, **kwargs):
staff_name = self.staff
this_staff = Staff.objects.get(name=staff_name)
name = this_staff.name
minus_value = self.leave_days_to_approve_now
if (self.Director_Authorization_Status == 'Approved'):
LeaveBalance.objects.filter(staff = name).update(Leave_current_balance=F('Leave_current_balance') - minus_value)
return super(NewLeave, self).save(*args, **kwargs)
else:
return super(NewLeave, self).save(*args, **kwargs)
In the above, you can see that I created a Director and Staff Model in which you can set as many staff and directors as you want in the Admin back-end. I created the staff Model because maybe not all of the staff will be users, so I think it is just a bit better to keep them separately in the DB from Users.
Important: first create the Director and Staff Models then migrate immediately since the other two tables will depend on them. Then you can create the other two Models.
I also do not think that in the LeaveBalance Model you should keep more things than what I put there. I think Year field for example redundant, since you can always filter by the date and date range in you want in the database.
Then the views (I used simple views only directly from the Models). With using these view classes you do not have to create Forms since it is automatically created from the Models and you can handle them with different functions/methods as Forms in the Views and in the Model classes.
from django.shortcuts import render, redirect
from django.http import HttpResponseRedirect, HttpResponse, HttpRequest
from django.urls import reverse
from django.views import View
from django.views.generic.detail import DetailView
from django.views.generic import ListView, TemplateView
from django.template import loader
from .models import NewLeave
from django.views.generic.edit import FormView, CreateView, DeleteView, UpdateView
from django.urls import reverse_lazy
class NewLeaveCreate(CreateView):
model = NewLeave
fields = '__all__'
def form_valid(self, form):
super().form_valid(form)
auth_status = form.cleaned_data['Director_Authorization_Status']
if (auth_status == 'Approved'):
return redirect('Myusers1:success_page')
elif (auth_status == 'Pending'):
return redirect('Myusers1:pending_success')
else:
return redirect('Myusers1:rejected_success')
class NewLeaveUpdate(UpdateView):
model = NewLeave
fields = '__all__'
class NewLeaveDelete(DeleteView):
model = NewLeave
success_url = reverse_lazy('newleave-list')
class NewLeaveDetail(DetailView):
model = NewLeave
template_name = 'myusers1/newleave_detail.html'
context_object_name = 'newleave'
queryset = NewLeave.objects.all()
def get_context_data(self, **kwargs):
context = super(NewLeaveDetail, self).get_context_data(**kwargs)
context['leave_details'] = NewLeave.objects.filter(pk=pk)
return context
class Success(TemplateView):
template_name = "authorizationsuccess.html"
class pending_success(TemplateView):
template_name = "pendingsuccess.html"
class rejected_success(TemplateView):
template_name = "rejectedsuccess.html"
Then in urls.py I defined the required urls:
from django.urls import path, re_path
from . import views
from . import models
app_name = 'Myusers1'
urlpatterns = [
path('newleave/add/', views.NewLeaveCreate.as_view(), name='newleave-add'),
path('newleave/<int:pk>/', views.NewLeaveUpdate.as_view(), name='newleave-update'),
path('newleave/<int:pk>/delete/', views.NewLeaveDelete.as_view(), name='newleave-delete'),
# path('newleave/add/<int:pk>/', views.NewLeaveDetail.as_view(), name='newleave-detail'),
path('newleave/add/success/', views.Success.as_view(), name='success_page'),
path('newleave/add/pendingleaves/', views.pending_success.as_view(), name='pending_success'),
path('newleave/add/rejectedleaves/', views.rejected_success.as_view(), name='rejected_success'),
]
I have not worked out all of the url paths.
And the templates like the newleave_form.html
{% extends 'myusers1/base.html' %}
{% block content %}
<div class"container">
<div class="col col-lg-2">
<h2>New Leave Form</h2>
<form method="post">
{% csrf_token %}
{{ form }}
<button type="submit">Authorize</button>
</form>
</div>
</div>
{% endblock %}
And there should be at least 3 different redirect template when a they submit a NewLeave form, authorized, pending, and rejected templates. I just give here the simple authorized success template:
{% extends 'myusers1/base.html' %}
{% block content %}
<h2>Thank you! The authorization of leave was successful</h2>
<div class="col-xs-12 .col-md-8"><li> Back to Home </li></div>
{% endblock %}
Do not forget to migrate and then register the models in the admin.py. Then you should create some staff in the database and few directors to try the above. I hope that the above can give you some direction to accomplish what you are up to with your project. With this I just wanted to give you a very simple example. (You have to create all of the other necessary templates and views).
If you create a new app for trying the above then in your project main urls.py file you should reference (include) your app urls like this with adding one extra line to your project' urls.py file. Then all of your new app urls has to be defined in your app's urls.py file:
This is how your main project's urls.py looks like then:
urlpatterns = [
path('admin/', admin.site.urls),
path('myusers1/', include('Myusers1.urls')),
# so in your case:
path('myapp/', include('myapp.urls')),
]
(you have to change the Myusers1 to your app name,)
And of course we could do a lot of other things with the Model Manager in Django: https://docs.djangoproject.com/en/2.1/topics/db/managers/
You have to call refresh_from_db on balance. like balance.refresh_from_db() to get the updated values from database.
Related
I'm running some tests for my app 'ads', but when I try to test the CreateView it fails with the following message:
AssertionError: 'just a test' != 'New title'
Here's the test:
class AdTests(TestCase):
def setUp(self):
self.user = get_user_model().objects.create_user(
username='test_user',
email='test#email.com',
password='secret'
)
self.ad = Ad.objects.create(
title='just a test',
text='Ehy',
owner=self.user
)
def test_ad_create_view(self):
response = self.client.post(reverse('ads:ad_create'), {
'title': 'New title',
'text': 'New text',
'owner': self.user.id,
})
self.assertEqual(response.status_code, 302)
self.assertEqual(Ad.objects.last().title, 'New title')
self.assertEqual(Ad.objects.last().text, 'New text')
So it could be that the test fails in creating a new ad, and then it compares the fields with the first ad in the setUp method.
I upload the rest of the code if it can help:
urls.py
from django.urls import path, reverse_lazy
from . import views
app_name='ads'
urlpatterns = [
path('', views.AdListView.as_view(), name='all'),
path('ad/<int:pk>', views.AdDetailView.as_view(), name='ad_detail'),
path('ad/create',
views.AdCreateView.as_view(success_url=reverse_lazy('ads:all')), name='ad_create'),
...
]
models.py
class Ad(models.Model) :
title = models.CharField(
max_length=200,
validators=[MinLengthValidator(2, "Title must be greater than 2 characters")]
)
price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
text = models.TextField()
"""We use AUTH_USER_MODEL (which has a default value if it is not specified in settings.py) to create a Foreign Key relationship between the Ad model
and a django built-in User model"""
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
comments = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Comment', related_name='comments_owned')
picture = models.BinaryField(null=True, editable=True)
tags = TaggableManager(blank=True)
content_type = models.CharField(max_length=256, null=True, help_text='The MIMEType of the file')
favorites = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Fav', related_name='favorite_ads')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
views.py
class AdCreateView(LoginRequiredMixin, View):
template_name = 'ads/ad_form.html'
success_url = reverse_lazy('ads:all')
def get(self, request, pk=None):
form = CreateForm()
ctx = {'form': form}
return render(request, self.template_name, ctx)
# Pull data
def post(self, request, pk=None):
form = CreateForm(request.POST, request.FILES or None)
if not form.is_valid():
ctx = {'form': form}
return render(request, self.template_name, ctx)
pic = form.save(commit=False)
pic.owner = self.request.user
pic.save()
form.save_m2m()
return redirect(self.success_url)
forms.py (the view uses it especially to check and save the image)
class CreateForm(forms.ModelForm):
max_upload_limit = 2 * 1024 * 1024
max_upload_limit_text = naturalsize(max_upload_limit)
# Call this 'picture' so it gets copied from the form to the in-memory model
# It will not be the "bytes", it will be the "InMemoryUploadedFile"
# because we need to pull out things like content_type
picture = forms.FileField(required=False, label='File to Upload <= ' + max_upload_limit_text)
upload_field_name = 'picture'
class Meta:
model = Ad
fields = ['title', 'text', 'price', 'picture', 'tags']
# Check if the size of the picture is less than the one specified (see above).
def clean(self):
cleaned_data = super().clean()
pic = cleaned_data.get('picture')
if pic is None:
return
if len(pic) > self.max_upload_limit:
self.add_error('picture', "File must be < " + self.max_upload_limit_text + " bytes")
# Convert uploaded File object to a picture
def save(self, commit=True):
instance = super(CreateForm, self).save(commit=False)
# We only need to adjust picture if it is a freshly uploaded file
f = instance.picture # Make a copy
if isinstance(f, InMemoryUploadedFile): # Extract data from the form to the model
bytearr = f.read()
instance.content_type = f.content_type
instance.picture = bytearr # Overwrite with the actual image data
if commit:
instance.save()
self.save_m2m()
return instance
I hope it is useful, thanks in advance!
According to Django Doc
Create View:
A view that displays a form for creating an object, redisplaying the form with validation errors (if there are any) and saving the object.
This is not a Valid way to do create View based on my experience. Check the doc Doc here.
if i understand what you are talking about You want to submit Ad Model using Create View, if you want to submit it in form You can something like this:
from django.views.generic import CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy,
class PostCreativeView(LoginRequiredMixin, CreateView):
model = #Your Model
fields = [#Fields of the model You want to submit]
template_name = #html template you want to submit the form
success_url = reverse_lazy(#url for redirected user when the form is submitted)
def form_valid(self, form):
form.instance.user = self.request.user
return super (PostCreativeView, self).form_valid(form)
in the form template you can add:
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save">
</form>
for styling you can follow this: Answer
this is my viewset:
class PollViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Poll.objects.all()
serializer_class = PollSerializer()
def get_queryset(self):
country = self.kwargs.get('pk', None)
if country is not None:
django_countries.Countries.name(country)
return self.queryset.filter(personality__country__name=country)
else:
country = self.request.user.preferred_country
return self.queryset.filter(personality__country=country)
model.py :
from django_countries.fields import CountryField
class Personality(models.Model):
name = models.CharField(max_length=100)
bio = models.TextField()
age = models.IntegerField()
class Gender(models.TextChoices):
MALE = 'MALE', _('Male')
FEMALE = 'FEMALE', _('Female')
OTHER = 'OTHER', _('Other')
gender = models.CharField(
max_length=6,
choices=Gender.choices,
default=Gender.MALE,
)
country = CountryField(null=True)
picture = models.ImageField(upload_to='uploads/profile_images/%Y/%m/%d')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Poll(RulesModel):
personality = models.OneToOneField(Personality, related_name='polls', on_delete=models.CASCADE)
start_date = models.DateTimeField(null=True)
end_date = models.DateTimeField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return 'poll#' + str(self.id) + ' ' + self.personality.name
Urls.py
from django.conf.urls.static import static
from django.urls import path, include
from liderate import settings
from .views import PollViewSet, PersonalityViewSet, PollListViewSet
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'polls', PollViewSet)
router.register(r'polls-list', PollListViewSet)
router.register(r'personalities', PersonalityViewSet)
urlpatterns = [
path('', include(router.urls)),
]
here it raises exception that "Unsupported lookup 'name' for CountryField or join on the field not permitted."
this is raised in the if condition (else condition works perfectly fine). is there any other way of filtering the query set based on the country's name
i actually want to view only the polls related to country passed as a param in the get url
I had a field named location_countries in my project model based on CountryField. It was a multiselect and I needed to create a search based on country name or any other information available in project's other fields. conventional ways e.g "filter(fieldname__icontains) or Q(fieldname__country__name) etc failed. After a lot of trouble, search and thinking, I finally devised a workaround that is working as I intended. Now by giving any keywords for example "samo" in my search field it returns all projects based in "American Samoa" or "afghan" and it retruns all projects based in "Afghanistan". Hope This helps you also. Search field in normal html input tag named "searchTxt". to avoid duplication i first converted result to set and then back to list.
def viewProjects(request):
if request.user.is_authenticated:
if 'searchTxt' in request.GET:
qry = request.GET['searchTxt']
countries_list = []
pros = Project.objects.all()
for pro in pros:
for i in pro.location_countries:
if qry.lower() in i.name.lower():
countries_list.append(pro.project_heading)
res=Project.objects.filter(project_heading__in=countries_list)
projs = Project.objects.filter(
Q(project_id__icontains=qry)|
Q(project_heading__icontains=qry) |
Q(project_description__icontains=qry)|
Q(sla_type__icontains=qry)|
Q(project_type__icontains=qry)|
Q(project_priority__icontains=qry)|
Q(location_countries__exact=qry)|
Q(location_cities__icontains=qry)|
Q(project_manager__username__icontains=qry)|
Q(escalation_manager__username__icontains=qry)
)
project = list(chain(res, projs))
prj = set(project)
projects=list(prj)
context = { 'projects':projects }
return render(request, 'admin_view_projects.html', context)
else:
projects = Project.objects.all()
context = { 'projects':projects }
return render(request, 'admin_view_projects.html', context)
else:
return redirect('index')
Here is my models.py file.
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from django.utils.text import slugify
import misaka
from django.contrib.auth import get_user_model
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=255, unique=True)
slug = models.SlugField(allow_unicode=True, unique=True)
description = models.TextField(blank=True, default='')
description_html = models.TextField(editable=False, default='',blank=True)
members = models.ManyToManyField(User, through="CategoryMember")
category_pic = models.ImageField(upload_to = 'category_pics', blank=True)
def __str__(self):
return self.name
# WE are saving the model. But before that we are converting
# the name using slugify and description using misaka.
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
self.description_html = misaka.html(self.description)
super().save(*args, **kwargs)
#get_absolute_url is used because it tell the template
# CreateView to go to the page it is directing.
# for this example it is directing to go to single page of a category.
def get_absolute_url(self):
return reverse("categories:single", kwargs={"slug":self.slug})
class Meta:
ordering = ['name']
class CategoryMember(models.Model):
category = models.ForeignKey(Category, related_name = "memberships", on_delete=models.CASCADE)
user = models.ForeignKey(User, related_name="user_categories", on_delete=models.CASCADE)
def __str__(self):
return self.user.username
class Meta:
unique_together= ("category", "user")
This is some part of views.py file
from django.contrib.auth.models import User
from categories.models import CategoryMember, Category
class UserPosts(ListView):
model = Post
template_name = 'posts/user_post_list.html'
def get_queryset(self):
try:
self.post_user = User.objects.prefetch_related("user_of_post_model").get(
username__iexact=self.kwargs.get("username")
)
except User.DoesNotExist:
raise Http404
else:
return self.post_user.user_of_post_model.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
current_user = UserProfileInfo.objects.filter(
user__id__iexact = self.post_user.id
).get()
i=1
user_categories={}
for member in CategoryMember.objects.all():
if member.user == self.post_user:
user_categories.update({i:member.category.name})
i=i+1
else:
print('not found')
print(user_categories)
if current_user.profile_pic:
profile_pic = True
picture = current_user.profile_pic
edited_picture = get_thumbnail(picture, '350x350', quality=99, format='PNG')
else:
profile_pic = False
root = settings.MEDIA_ROOT
import os
root1 = os.path.join(root, 'profile_pics/no-image.png')
picture = root1
edited_picture = get_thumbnail(picture, '350x350', quality=99, format='PNG')
# resizeimage.resize_cover(picture, [200, 100], validate=False)
# rescale_image(picture,width=100,height=100)
current_user1 = current_user
user_info = {
'current_user':current_user,
'user_categories':user_categories,
'picture':edited_picture,
'profile_pic':profile_pic,
'post_user':self.post_user,
}
context['user_info'] = user_info
return context
After rendering the html page i can see that dictionary has values inside. Here is the values printed in terminal.
not found
not found
not found
not found
not found
{1: 'Regression', 2: 'Classification'}
Now, i want to get user_categories in my django template. i have tried in different approaches but can't get user_categories .
this is some part of my django html file.
<h3>Member of Categories</h3>
{# this line shows error #}
{# {{ userinfo[{{user_categories}}] }} #}
{{ userinfo.user_categories.1 }}
{% for categories in userinfo.user_categories %}
{{categories}}
{% endfor %}
<h5 class="techfont">Post written by {{user_info.post_user.username}} : {{post_list.count}} </h5>
This section only renders user_info.post_user.username but not user_info.user_categories.1
see the text below.
Member of categories.
Post written by abc: 1
user_categories is a dictionary, not a list. So try like this:
{% for idx, categories in userinfo.user_categories.items %}
{{idx}}. {{categories}}
{% endfor %}
models.py
from django.db import models
from django.contrib.auth.models import User
CHOICES = (('1','Earned Leave'),('2','Casual Leave'),('3','Sick Leave'),('4','Paid Leave'))
class Leave(models.Model):
name = models.CharField(max_length = 50)
user = models.ForeignKey(User, on_delete = models.CASCADE, null =True)
employee_ID = models.CharField(max_length = 20)
department = models.CharField(max_length = 50)
designation = models.CharField(max_length = 50)
type_of_leave = models.CharField(max_length = 15, choices = CHOICES, default = None)
from_date = models.DateField(help_text = 'mm/dd/yy')
to_date = models.DateField(help_text = 'mm/dd/yy')
reporting_manager = models.CharField(max_length = 50, default = None, help_text = '0001_manager, 0002_manager')
reason = models.CharField(max_length= 180)
accepted = models.BooleanField(('approval status'), default= False)
def __str__(self):
return self.name
I want the user to only access the 'type_leave_field' after submitting the form, when he is given 'is staff' status.
forms.py
from django import forms
from lrequests import models
class LeaveRequestForm(forms.ModelForm):
class Meta:
fields = ("name", "employee_ID", "department", "designation", "type_of_leave", "from_date", "to_date", "reporting_manager", "reason")
model = models.Leave
views.py
from django.shortcuts import render
from django.http import HttpResponse
from .forms import LeaveRequestForm
from .models import Leave
def leaveRequest(request):
form_class = LeaveRequestForm
if request.method == "POST":
form = LeaveRequestForm(request.POST)
if form.is_valid():
leave = form.save(commit = False)
leave.user = request.user
form.save()
return HttpResponse("Sucessfully submitted")
else:
form = LeaveRequestForm()
return render(request, "request_form.html", {'form' : form_class})
I've tried using django-guardian and django-objectpermissions, but I've not seen the exact way of using them. I've even created a user-permission for it, but as the admin views and templates are not user-written code, I couldn't give a defnition.
base : It is a leave management system project, where a user can edit his type of leave even after submitting the form, thanks.
update -
How do I display particular fields in the html page.
from my models.py the 'accepted' field is a Boolean field, after the user submits the form I want to display the field's status , whether it is True or False in a html page. I'm unable to retrieve that field from the db..
What you can do is override the ModelAdmin readonly_fields method by making all other fields read only except the one the user should be able to update.
def get_readonly_fields(self, request, obj=None):
# the logged in user can be accessed through the request object
if obj and request.user.is_staff:
readonly_fields = [f.name for f in self.opts.fields]
readonly_fields.remove('type_leave_field')
return readonly_fields
Regarding the list comprehension:
self.opts gives you access to the Model specified in your Meta class (or #admin.register() should also be fine).
self.opts.fields is a tuple consisting of field objects from that model. Each object is a dictionary with meta data of a field. By accessing the .name (key) of each field object we can get the field name represented as a string.
I'm creating an availability app for Volunteer Firefighters and cannot get one of the model fields to display in the template:
models.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Team(models.Model):
name = models.CharField('Name', max_length = 200)
def __str__(self):
return self.name
class Firefighter(models.Model):
RANKS = (
('CFO', 'Chief Fire Officer'),
('DCFO','Deputy Chief Fire Officer'),
('SSO', 'Senior Station Officer'),
('SO', 'Station Officer'),
('SFF', 'Senior Firefighter'),
('QFF', 'Qualified Firefighter'),
('FF', 'Firefighter'),
('RFF', 'Recruit Firefighter'),
('OS', 'Operational Support'),
)
STATUS_OPTIONS = (
('AV', 'Available'),
('OD', 'On Duty'),
('UN', 'Unavailable'),
('LV', 'Leave'),
)
rank = models.CharField("Rank", max_length = 50 , choices=RANKS, default='RFF')
first_name = models.CharField("First Name", max_length = 200)
last_name = models.CharField("Last Name", max_length = 200)
start_date = models.DateField(name="Start Date")
status = models.CharField("Status", max_length = 20 , choices=STATUS_OPTIONS, default='Available')
driver = models.BooleanField('Driver', default=False)
officer = models.BooleanField('Officer Qualified', default=False)
teams = models.ManyToManyField(Team)
def __str__(self):
return self.first_name + ' ' + self.last_name
class Meta:
verbose_name = "Firefighter"
verbose_name_plural = "Firefighters"
views.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
from django.forms import ModelForm
from .models import Firefighter
from django.http import HttpResponseRedirect
# Create your views here.
#login_required(login_url='/login/')
def status(request):
firefighters = Firefighter.objects.all()
context = {
'firefighters': firefighters,
'availableCount': firefighters.filter(status='AV').count() + firefighters.filter(status='OD').count(),
'leaveCount': firefighters.filter(status='LV').count(),
'unAvCount': firefighters.filter(status='UN').count()
}
return render(request, 'status.html', context)
#login_required(login_url='/login/')
def details(request, id):
class ChangeStatus(ModelForm):
class Meta:
model = Firefighter
fields = ['status']
form = ChangeStatus()
firefighter = Firefighter.objects.get(id=id)
if request.method == 'POST':
form = ChangeStatus(request.POST, instance=firefighter)
if form.is_valid():
form.save()
return HttpResponseRedirect('/status/')
else:
form = ChangeStatus()
context = {
'firefighter': firefighter,
'form': form
}
return render(request, 'details.html', context)
def members(request):
firefighters = Firefighter.objects.all().order_by('rank')
context = {
'firefighters': firefighters,
}
return render(request, 'members.html', context)
and the members.html that won't render correctly:
{% extends "base.html" %}
{% block content %}
<ul>
{% for firefighter in firefighters %}
<li> {{firefighter.rank}} {{firefighter.first_name}} {{firefighter.last_name}} {{firefighter.start_date}}</li>
{% endfor %}
</ul>
{% endblock content %}
The members.html will correctly render firefighter.first_name and firefighter.last_name but nothing displays for firefighter.start_date even though each firefighter has a specified start date in the database.
Any ideas as to why?
Bonus question: Is there any way for me to sort the ranks as they are listed in the model when querying the database?
Where you define the field for the start_date you have:
start_date = models.DateField(name="Start Date")
It should be:
start_date = models.DateField("Start Date")
The first arg in Date_Field is verbose_name which will automatically set the name with a more acceptable snake_case version of the name.
As far as your ordering, try setting variables for your choices with a value that will sort.
CFO = ‘A’
DCFO = ‘B’
...
RANK_CHOICES = (
(CFO, ‘Chief Fire Officer’),
(DCFO, ‘Deputy Chief Fire Officer’)
(...)
)
If you always wanted query results by rank:
class Meta:
ordering = [‘rank’]
If not, use the current query you have.
As mentioned in Zev’s answer “name=‘label’” is not valid for any field. It is verbose_name=‘label. Depending on the field you can put the label as the first argument in quotes, but other fields you have to use verbose_name.