Django model foreign key to Superuser - python

I have a django model Request which has a field Approver which is set to the User which has the superuser status. I would like to assign value to this field automatically such that it rotates among the project admins. Any suggestions are welcome. Thanks in advance.

A easy solution that comes to my mind is just in the html template that uses the request model, you can do a function that evaluates a possible asigned user.. something like that:
1. Add a field in your request model that is a foreign key to your user model
Example:
class Request(models.Model):
some other code...
assigned_user=models.ForeignKey(User, on_delete=models.CASCADE)
And add an asigned boolean in your User Model:
class User(models.Model):
assigned=models.BooleanField(default=False)
Then in your html code for creating a request:
You can do something like:
{% for user in Users %}
{% if user.isAdmin %}
{% if not user.assigned %}
<input name="request.varName">{{User.id}}</input>
{% endif %}
{% endif %}
{% endfor %}
Remember that in your view you have to call the request model and your Users model.. You can achieve this in this way:
class RequestCreate(CreateView):
... some other code..
def get_context_data(self, **kwargs):
context = super(RequestCreate, self).get_context_data(**kwargs)
context['Users'] = User.objects.all()
#context['venue_list'] = Venue.objects.all()
#context['festival_list'] = Festival.objects.all()
# And so on for more models
return context
I hope to be usefull here.

You can pass functions as defaults. Define a function like so
def default_func():
last_assigned = Request.objects.latest().Approver # gets latest assigned Approver
# the below if statement always looks for the next superuser pk
if User.objects.filter(is_superuser=True, pk__gt=last_assigned.pk).exists():
next_assigned_superuser = User.objects.filter(is_superuser=True, pk__gt=last_assigned.pk)\
.order_by('pk')[0]
# if there isn't a superuser with a higher pk than the last_assigned_superuser, it will choose
# the superuser below or equal to it with the lowest pk. This will handle the case where there is
# only one superuser without any fuss.
else:
next_assigned_superuser = User.objects.filter(is_superuser=True, pk__lte=last_assigned.pk) \
.order_by('pk')[0]
return next_assigned_superuser
and add this to your Approver field:
# you must pass the function without parenthesis at the end, or else it will
# set the default to whatever the value is at server run time.
# If you pass the function itself, it will be evaluated every time a
# default is needed.
Approver = models.ForeignKey(User, default=default_func)
Edit: Added a return value to default_func. Whoops.

I wrote a function in the Admin form which sets the approver to the User which has the least ammount of requests. Works fine for me.
def get_recipient(self):
approvers = User.objects.annotate(num_of_requests=models.Count('model_field_related_name')).order_by('num_of_requests')
return approvers.first()

Related

Django CreateView with get_success_url not working for this specific case

I'm using Django 2.1.
I'm having a problem with a CreateView because I need to redirect to the update url, but that url contains one argument that is created manually after verifying that the form is valid.
This is the view code:
class ProjectCreateInvestmentCampaignView(LoginRequiredMixin, SuccessMessageMixin, generic.CreateView):
template_name = 'webplatform/project_edit_investment_campaign.html'
model = InvestmentCampaign
form_class = CreateInvestmentCampaignForm
success_message = 'Investment campaign created!'
def get_success_url(self):
return reverse_lazy('project-update-investment-campaign',
args=(self.kwargs['pk'], self.object.campaign.pk, self.object.pk))
def form_valid(self, form):
project = Project.objects.get(pk=self.kwargs['pk'])
form.instance.investment_type = "A"
form.instance.contract_type = "CI"
form.instance.history_change_reason = 'Investment campaign created'
valid = super(ProjectCreateInvestmentCampaignView, self).form_valid(form)
if valid:
campaign = CampaignBase.objects.create(project=project, )
form.instance.campaign = campaign
form.instance.campaign.project = project
form.instance.campaign.creation_date = timezone.now()
form.save()
return valid
As you can see, on the form_valid I validate first the form, and then I create the object campaign and assign all the related data. This is working fine.
The problem came when I changed the get_success_url to fit my use case, that is redirecting to the update view.
I debugged and saw that at the moment I create the variable valid on the form_valid, it checks the success url, and that triggers me the following error:
Exception Type: AttributeError
Exception Value:
'NoneType' object has no attribute 'pk'
Exception Location: /Volumes/Archivos/work/i4b/webplatform/views/investor_campaign_views.py in get_success_url, line 25
I asume that the error is because the campaign is not created yet so it's trying to get the pk from a non existing object.
The thing is that I cannot create the campaign if the form is not validated, but I need the campaign to make the url working (that url is working as it is on the UpdateView that I already have).
It will only invoke get_success_url after form_valid. So it's up to form_valid to create and save the objects needed. If it's valid for them not to be created, you need a different approach. Maybe initialize (say) self.campaign_pk = 0, update it if a campaign can be created with the pk of the campaign object, and let the next view sort out what to do when pk==0. Or,
...
args=(self.kwargs['pk'],
self.object.campaign.pk if self.object.campaign else 0,
self.object.pk))
(I don't fully follow your code so I might be barking up the wrong tree here)
It may be that you don't want CreateView but FormView, which doesn't handle object creation for you, so you may find greater flexibility over how to process a valid form that nevertheless cannot be fully honoured all the time. Or even, just a plain old function-based view, in which you can process two or more forms and be far more able to decide on conditions that constitute non-validity even after all the forms have technically validated.
This is a function-based view structure I have used where I have two forms to process, and a fairly long but boring set of operations to do after BOTH forms validate:
def receive_view( request):
# let's put form instantiation in one place not two, and reverse the usual test. This
# makes for a much nicer layout with actions not sandwiched by "boilerplate"
# note any([ ]) forces invocation of both .is_valid() methods
# so errors in second form get shown even in presence of errors in first
args = [request.POST, ] if request.method == "POST" else []
batchform = CreateUncWaferBatchForm( *args, layout=CreateUncWaferBatchLayout )
po_form = CreateUncWaferPOForm( *args, layout = CreateUncWaferPOLayout, prefix='po')
if request.method != "POST" or any(
[ not batchform.is_valid(), not po_form.is_valid() ]):
return render(request, 'wafers/receive_uncoated.html', # can get this out of the way at the top
{'batchform': batchform,
'po_form': po_form,
})
#it's a POST, everything is valid, do the work
...
return redirect('appname:viewname', ...)
For me, get_success_url was not invoked as the form was not valid (was invalid) and I didn't know. You can override form_invalid(self, form) to control the behavior.
Also, consider this block of code to show any errors in your template
{% if form.errors %}
<div class="alert alert-danger" role="alert">
{% for field, errors in form.errors.items %}
{% for error in errors %}
<b>{{ field }}</b>: {{ error }}
{% endfor %}
{% endfor %}
</div>
{% endif %}

Using one model to filter another model in Django

I'm trying to access the information in my gadb_action model based on the action_ids in my gadb_vote model. I'm initially only getting the information for a particular legislator and then attempting to get the bills associated with the actions that legislator has voted on.
Right now, my action_list is only storing action_ids, but not the related information from the gadb_action model that I want to use in my template.
What is the best way to store that information outside of the for loop to be accessed by the template? Is there a way to write to an empty QuerySet?
Thanks in advance for any and all help!
view
def each_member(request,legislator_id):
each_member = get_object_or_404(gadb_legislator, legislator_id=legislator_id)
each_vote = gadb_vote.objects.filter(legislator_id=legislator_id)
action_list = []
for i in each_vote:
action = gadb_action.objects.filter(action_id=i.action_id)
action_list.append(action)
context = {
'each_member': each_member,
'each_vote': each_vote,
'action_list': action_list
}
return render(request, "eachmember.html", context)
models
class gadb_action(models.Model):
action_id = models.IntegerField(unique=False, max_length=4, primary_key=True)
bill_id = models.IntegerField(unique=False, max_length=12)
class gadb_vote(models.Model):
vote_id = models.IntegerField(unique=False, max_length=11,primary_key=True)
legislator_id = models.IntegerField(unique=False, max_length=11)
action_id = models.IntegerField(unique=False, max_length=11)
template
{% for i in action_list %}
{{i.bill_id}}
{{i.action_id}}
{% endfor %}
Your models are broken.
For a start, although it's not directly related to the question, you need to define your primary keys as AutoFields so that they are autoincremented every time a new entity is added. Otherwise you'll get all sorts of errors when you save a new row. (Even better, don't define the PK at all, and let Django add it automatically.)
Secondly, as lalo says, you should have ForeignKeys from Action to Bill, and from Vote to Action and Vote to Legislator. That way you can get the relevant information with a single query, and follow the foreign keys as required in your template.
(Also, Django already includes the app name in the underlying table name: no need to prefix everything with 'gadb'.)
class Action(models.Model):
bill = models.ForeignKey(Bill)
class Vote(models.Model):
legislator = models.ForeignKey(Legislator)
action = models.ForeignKey(Action)
View:
def each_member(request,legislator_id):
actions = Action.objects.filter(vote__legislator_id=legislator_id)
return render(request, "eachmember.html", {'action_list': actions})
Template:
{% for action in actions %}
{{ action.bill.name }}
{{ action.someotherfield }}
{% endfor %}

Extend Model's Queryset with additional attributes

I have simple Django model with some internet sessions (Radius logs). And want to show it in my template.
models.py:
class Radacct(models.Model):
radacctid = models.BigIntegerField(primary_key=True)
username = models.CharField(max_length=253)
nasipaddress = models.IPAddressField()
class Meta:
db_table = u'radacct'
view.py:
def ajax_radius_sessions(request, username):
radius_sessions = Radacct.objects.filter(username=username).order_by('-radacctid')
template.html:
{% for session in radius_sessions %}
{{ session.username }}, {{ session.nasipaddress }}</br>
{% endfor %}
In my template, I need to show the hostname based on user's ip-address (nasipaddress) as well.
Method 1:
Creation of Model's method.
I do not want to calculate Hostname as Model's method, because it will be triggered for every record, but the number of session for particular user can be very big — in this case it will cause huge amount of DNS-checks. 1000 sessions = 1000 DNS checks?..
Method 2a:
View level.
I was trying to this on View level, to check only unique IPs and get the "local" dictionary with IP-Hostname pairs:
#servers={'192.168.100.1':'alpha', '192.168.100.2':'beta', '192.168.100.3':'gamma'}
But I can not access this dictionary in the template, using the key as variable:
#Wrong!
{{ servers[session.nasipaddress] }}
Method 2b:
View level. Adding new attribute to the Queryset instance.
Maybe I can add a new attribute to my Radacct Model which is not connected with database. And fill it by hostname, in my View?
What is the proper way to calculate some "attribute" and then access it in the Template in Queryset {% for %} loop?
Again: it seems I can not do this as Model's method, so I think I should extend my database results with custom ones.
P.S. Sorry for a really looong post. This is my very first try on Stackoverflow. Thank you!
Model "method". Create a separate class that is responsible for looking up DNS entries and caching them, and refer to an instance of this class in a descriptor on the model.

Properly Defining Relationships in Django DB

I'm working on a fitness game web app. The idea is that each week has a certain number of tasks that a person must complete before the next week is revealed. Here is my models.py schema for the app so far:
from django.db import models
from django.contrib.auth.models import User
class WeekOne(models.Model):
squats = models.PositiveIntegerField()
lunges = models.PositiveIntegerField()
skipStairs = models.BooleanField()
stairDaysCount = models.PositiveSmallIntegerField()
# Set to true if (squats == 1000), (lunges == 250),
# (skipStairs is True), and (stairDaysCount == 3)
weekOneComplete = models.BooleanField()
class UserProfile(models.Model):
user = models.OneToOneField(User)
weekOne = models.ForeignKey(WeekOne)
I'm lost on a few points. First of all, obviously I want each user to be able to track their own progress and not have any other users be able to see it. Is making weekOne a ForeignKey the best way to do this? If so, how does accessing each user's data work once this relationship is defined? For example, if I wanted to create an addSquats function, would I do something like this:
user = UserProfile()
user.squats += 5
or would I have to do some other magic to get to the squats field?
Second, every time a user makes a change to their progress (i.e., adds a squat or lunge), I want to check if all of the fields have met a certain a benchmark. If they have, I want to set weekOneComplete to true. Each addition will be triggered by some javascript when a user clicks a button -- but where would I put the function that checks/updates the database?
First of all, obviously I want each user to be able to track their own progress and not have any other users be able to see it.
In the template, you can do like this
User: {{ user.username }}
squats: {{ user.userprofile.squats }}
lunges: {{ user.userprofile.lunges }}
Skip Stairs:
{% if user.userprofile.skipStairs %}
Yes
{% else %}
No
{% endif %}
Stair: {{ user.userprofile.stairDaysCount }}}
Week Completed:
{% if user.userprofile.weekOneComplete %}
Yes
{% else %}
In-Progress
{% endif %}
How does accessing each user's data work once this relationship is defined? For example, if I wanted to create an addSquats function.
To access user data,
user = UserProfile(user=request.user)
user.weekOne.squats += 5
user.weekOne.save()
user.save()
Second, every time a user makes a change to their progress (i.e., adds a squat or lunge), I want to check if all of the fields have met a certain a benchmark. If they have, I want to set weekOneComplete to true.
Under your UserProfile model, create check updates function.
class UserProfile(models.Model):
user = models.OneToOneField(User)
weekOne = models.ForeignKey(WeekOne)
def check_updates(self):
check = WeekOne.object.get(id=self.weekOne)
if check.skipStairs and \
check.squats == 1000 and \
check.lunges == 250 and \
check.stairDaysCount == 3:
check.weekOneComplete = True
check.save()
So every time you update the data for that user, just call the method like this:
user = UserProfile.objects.get(user=request.user)
user.check_updates()
To access field of week one you need to do:
user.weekOne.squats += 5
user.weekOne.save()
Actually, it is better to use the F function and do (or similar):
user.weekOne.squats = F('squates') + 5
In general, it is better to do all checkings also in the server side and not to rely on the client (JS or whatever). E.g You expose a url and check for all the POST params that they are integers (e.g. converted to ints)
To your second question: one possible (and simple) way of doing this, since you say you're using javascript, is through an ajax call.
It should point to a url which, itself, points to a view that'll handle the data you're sending in the request. Something along the lines of (say you're using a POST request):
def add(request):
if not request.is_ajax():
raise ...
excercise, amount = request.POST['excercise'], request.POST['amount']
user = request.user
# a model method that'll add to whatever activity
# the user did and update the "week one complete" field
user.did_activity(excercise, amount)
if user.weekOne.weekOneComplete:
return HttpResponse(json.dumps(some_return_data), mimetype="application/json")
return HttpResponse(json.dumps(other_return_data), mimetype="application/json")
This is more to the side of pseudocode, so you get the idea. You'd still need to write the ajax call on the JS side, the model method that'll add to the correct excercise and validate the benchmark, and save the user to the database. For example:
def did_activity(self, excercise, amount):
if excercise == 'squats':
self.weekOne.squats += amount
...
if self.hit_benchmark():
self.weekOne.weekOneComplete = True
self.save()
Also, this example assumes the user is authenticated.
Good luck.

Model limit_choices_to={'user': user}

I went to all the documentation, also I went to the IRC channel (BTW a great community) and they told me that is not possible to create a model and limit choices in a field where the 'current user' is in a ForeignKey.
I will try to explain this with an example:
class Project(models.Model):
name = models.CharField(max_length=100)
employees = models.ManyToManyField(Profile, limit_choices_to={'active': '1'})
class TimeWorked(models.Model):
project = models.ForeignKey(Project, limit_choices_to={'user': user})
hours = models.PositiveIntegerField()
Of course that code doesn't work because there is no 'user' object, but that was my idea and I was trying to send the object 'user' to the model to just limit the choices where the current user has projects, I don't want to see projects where I'm not in.
Thank you very much if you can help me or give me any advice, I don't want to you write all the app, just a tip how to deal with that. I have 2 days with this in my head and I can't figure it out :(
UPDATE: The solution is here: http://collingrady.wordpress.com/2008/07/24/useful-form-tricks-in-django/ sending request.user to a model.
This limiting of choices to current user is a kind of validation that needs to happen dynamically in the request cycle, not in the static Model definition.
In other words: at the point where you are creating an instance of this model you will be in a View and at that point you will have access to the current user and can limit the choices.
Then you just need a custom ModelForm to pass in the request.user to, see the example here:
http://collingrady.wordpress.com/2008/07/24/useful-form-tricks-in-django/
from datetime import datetime, timedelta
from django import forms
from mysite.models import Project, TimeWorked
class TimeWorkedForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
super(ProjectForm, self).__init__(*args, **kwargs)
self.fields['project'].queryset = Project.objects.filter(user=user)
class Meta:
model = TimeWorked
then in your view:
def time_worked(request):
form = TimeWorkedForm(request.user, request.POST or None)
if form.is_valid():
obj = form.save()
# redirect somewhere
return render_to_response('time_worked.html', {'form': form})
Model itself doesn't know anything about current user but you can give this user in a view to the form which operates models objects (and in form reset choices for necessary field).
If you need this on admin site - you can try raw_id_admin along with django-granular-permissions (http://code.google.com/p/django-granular-permissions/ but I couldn't rapidly get it working on my django but it seems to be fresh enough for 1.0 so...).
At last, if you heavily need a selectbox in admin - then you'll need to hack django.contrib.admin itself.
Using class-based generic Views in Django 1.8.x / Python 2.7.x, here is what my colleagues and I came up with:
In models.py:
# ...
class Proposal(models.Model):
# ...
# Soft foreign key reference to customer
customer_id = models.PositiveIntegerField()
# ...
In forms.py:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.forms import ModelForm, ChoiceField, Select
from django import forms
from django.forms.utils import ErrorList
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _
from .models import Proposal
from account.models import User
from customers.models import customer
def get_customers_by_user(curUser=None):
customerSet = None
# Users with userType '1' or '2' are superusers; they should be able to see
# all the customers regardless. Users with userType '3' or '4' are limited
# users; they should only be able to see the customers associated with them
# in the customized user admin.
#
# (I know, that's probably a terrible system, but it's one that I
# inherited, and am keeping for now.)
if curUser and (curUser.userType in ['1', '2']):
customerSet = customer.objects.all().order_by('company_name')
elif curUser:
customerSet = curUser.customers.all().order_by('company_name')
else:
customerSet = customer.objects.all().order_by('company_name')
return customerSet
def get_customer_choices(customerSet):
retVal = []
for customer in customerSet:
retVal.append((customer.customer_number, '%d: %s' % (customer.customer_number, customer.company_name)))
return tuple(retVal)
class CustomerFilterTestForm(ModelForm):
class Meta:
model = Proposal
fields = ['customer_id']
def __init__(self, user=None, *args, **kwargs):
super(CustomerFilterTestForm, self).__init__(*args, **kwargs)
self.fields['customer_id'].widget = Select(choices=get_customer_choices(get_customers_by_user(user)))
# ...
In views.py:
# ...
class CustomerFilterTestView(generic.UpdateView):
model = Proposal
form_class = CustomerFilterTestForm
template_name = 'proposals/customer_filter_test.html'
context_object_name = 'my_context'
success_url = "/proposals/"
def get_form_kwargs(self):
kwargs = super(CustomerFilterTestView, self).get_form_kwargs()
kwargs.update({
'user': self.request.user,
})
return kwargs
In templates/proposals/customer_filter_test.html:
{% extends "base/base.html" %}
{% block title_block %}
<title>Customer Filter Test</title>
{% endblock title_block %}
{% block header_add %}
<style>
label {
min-width: 300px;
}
</style>
{% endblock header_add %}
{% block content_body %}
<form action="" method="POST">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Save" class="btn btn-default" />
</form>
{% endblock content_body %}
I'm not sure that I fully understand exactly what you want to do, but I think that there's a good chance that you'll get at least part the way there using a custom Manager. In particular, don't try to define your models with restrictions to the current user, but create a manager that only returns objects that match the current user.
Hmmm, I don't fully understand your question. But if you can't do it when you declare the model maybe you can achieve the same thing with overriding methods of the class of objects where you "send" the user object, maybe start with the constructor.
Use threadlocals if you want to get current user that edits this model. Threadlocals middleware puts current user into process-wide variable. Take this middleware
from threading import local
_thread_locals = local()
def get_current_user():
return getattr(getattr(_thread_locals, 'user', None),'id',None)
class ThreadLocals(object):
"""Middleware that gets various objects from the
request object and saves them in thread local storage."""
def process_request(self, request):
_thread_locals.user = getattr(request, 'user', None)
Check the documentation on how to use middleware classes. Then anywhere in code you can call
user = threadlocals.get_current_user

Categories

Resources