How implement user matching algorithm into Django app - python

I'm creating a Django app that allows users to register as either a "mentor" or "mentee". Each user has some information stored in the User that is common across all accounts, while mentors/mentees have a second table (one-to-one) MentorProfile/MenteeProfile with areas of expertise/interest.
The goal is to create a mechanism by which a mentor is assigned to a mentee after running a matching algorithm (such as stable relationship). I have working registration/edit features, however am stuck on how to begin implementing the match.
Is there a way to introduce a button into the Django admin panel that, when clicked:
Pulls the necessary information from the Django app
Makes the Mentor/Mentee matches and assignments
Updates the MentorProfile and MenteeProfile tables with these matches
For what it's worth, we have a working python script that pulls the necessary information from a csv that can make the mentor/mentee relationship assignment. We are simply unsure on how to implement this logic into our app.
EDIT:
Just a general view of my models. Additionally, the User class has the default user attributes (username, firstname, lastname, email, etc.)
Here is my blog/models.py
class User(AbstractUser):
is_mentor = models.BooleanField('Mentor', default=False)
is_mentee = models.BooleanField('Mentee', default=False)
class MenteeProfile(models.Model):
CAREER_CHOICES = [
('Doctor', 'Doctor'),
('Teacher', 'Teacher'),
('Engineer', 'Engineer'),
('Scientist', 'Scientist'),
('Biologist', 'Biologist'),
('Business', 'Business')
]
user=models.OneToOneField(User, on_delete=models.CASCADE)
career_interest1 = models.CharField(max_length=30, default='Doctor', choices=CAREER_CHOICES)
career_interest2 = models.CharField(max_length=30, default='Doctor', choices=CAREER_CHOICES)
career_interest3 = models.CharField(max_length=30, default='Doctor', choices=CAREER_CHOICES)
class MentorProfile(models.Model):
user=models.OneToOneField(User, on_delete=models.CASCADE)
AREA_OF_EXPERTISE = [
('Doctor', 'Doctor'),
('Teacher', 'Teacher'),
('Engineer', 'Engineer'),
('Scientist', 'Scientist'),
('Biologist', 'Biologist'),
('Business', 'Business')
('--', '--') ]
career_expertise1 = models.CharField(max_length=30, default='--', choices=AREA_OF_EXPERTISE)
career_expertise2 = models.CharField(max_length=30, default='--', choices=AREA_OF_EXPERTISE)
career_expertise3 = models.CharField(max_length=30, default='--', choices=AREA_OF_EXPERTISE)
career_expertise4 = models.CharField(max_length=30, default='--', choices=AREA_OF_EXPERTISE)
career_expertise5 = models.CharField(max_length=30, default='--', choices=AREA_OF_EXPERTISE)
career_expertise6 = models.CharField(max_length=30, default='--', choices=AREA_OF_EXPERTISE)
def __str__(self):
return self.user.username
Thanks!
Every Help Will Be Appreciated!

From django docs:
In admin.py file:
class MentorProfileAdmin(admin.ModelAdmin):
#define your list display or fieldsets
....
....
#now need to define urls for custom button in admin template file
def get_urls(self):
"""
generate urls for methods. and attach with admin url
:param self:
"""
urls = super().get_urls()
my_urls = [
url(r'^match-mentee/$', self.match_mentee),
]
return my_urls + urls
def match_mentee(self, request):
"""
here you put the matching logic
"""
....
....
self.message_user(request, "mentor has been assigned to mentee")
return HttpResponseRedirect("../")
For the addition of a custom button in admin template, there are limits so far as I remember when I needed to customise it in django 1.8. So only limited part of admin template can be customised. I would like you to refer the official docs to explore for customisation.
Also, put the url in in button's href to make button working in template.

Related

Django viewflow custom permissions

Probably something simple. I am trying to follow the cookbook example on the following link https://github.com/viewflow/cookbook/tree/master/guardian. With the exception of a couple of unrelated differences between the example and my own code (I am not using frontend and am using custom views). Everything else works as expected. I do not understand what I am getting wrong on the permissions side of things.
I am getting a "403 forbidden" error whenever a user other than the one that started the process tries to interact with the flow. This happens irrespective of the assigned user's assigned permissions - is this the expected behavior or should I open a ticket on Github?
While I am trying to understand if viewflow can support what I am trying to accomplish - I would like to leave apply the permissions checking on my own views (rather than the built in checks). I saw that there was a pull request https://github.com/viewflow/viewflow/issues/252 - however, I do not understand how to implement it.
Any help would be appreciated! Been stuck on this for quite a while
The permissions are defined in a custom user class
accounts/models.py
class Department(models.Model):
name = models.CharField(unique=True, max_length=250)
description = models.TextField(blank=True)
objects = managers.DepartmentManager()
class Meta:
permissions = [
('can_accept_bill', 'Can accept department bill'),
('can_validate_bill', 'Can validate department bill'),
('can_set_bill_paydate', 'Can set payment date for department bill'),
('can_pay_bill', 'Can pay department bill'),
flows.py
class OrderFlow(Flow):
process_class = models.OrderProccess
task_class = models.OrderTask
lock_impl = select_for_update_lock
start = (
flow.Start(
views.StartView)
.Permission(auto_create=True)
.Next(this.approve_budget)
)
approve_budget = (
flow.View(
views.BudgetApprovalView)
# .Permission(auto_create=True)
.Permission(
'order.can_accept_bill',
obj= lambda act: act.process.order.department
)
.Assign(lambda act: act.process.created_by)
.Next(this.check_budget_approval)
)
check_budget_approval = (
flow.If(
cond=lambda act: act.process.order.budgetholder_approved
)
.Then(this.approve_finance)
.Else(this.approve_budget)
)
approve_finance = (
flow.View(
views.FinanceApprovalView)
.Permission(auto_create=True)
.Assign(lambda act: act.process.created_by)
.Next(this.check_finance_approval)
)
models.py
class Order(models.Model):
department = models.ForeignKey(account_models.Department, on_delete=models.CASCADE)
description = models.CharField(max_length=30)
project = models.ForeignKey(project_models.Project, on_delete=models.CASCADE)
# other unrelated code
class OrderProccess(Process):
order = models.ForeignKey(Order, blank=True, null=True, on_delete=models.CASCADE)
class OrderTask(Task):
class Meta:
proxy = True
views.py
class StartView(StartFlowMixin, generic.UpdateView):
model = models.Order
form_class = forms.OrderForm
def get_object(self):
return self.activation.process.order
# other form handling code
class OrderView(FlowMixin, generic.UpdateView):
def get_object(self):
return self.activation.process.order
class BudgetApprovalView(FlowMixin, generic.UpdateView):
form_class = forms.BudgetHolderApproval
def get_object(self):

Find entities for specific user through multiple tables in Django Admin

I searched through stackoverflow about this particular scenario, but could not find a concrete answer, so i'm posting this.
So my problem is that i need to display specific records to a specific user in Django Admin. I'm aware that i can get the concrete logged in user through the get_queryset method extracting it from the request object. But the issue is i need to look through 6 tables to get to the information about the user of the recommendations so i could know which recommendation to display to him.
For example, if the records i need to display come from a Recommendation table, it has a reference to TableA, which has a reference to TableB .... which has a reference to TableF which has a reference to the User.
I'm aware i could do this by executing a plain SQL query with multiple joins, but my guess is that there must be a pythonic or Django sophisticated solution to this. But i may be wrong.
The model is unfortunately not in my control, nor i can change it, so i'm left to work with the state of the model that there is.
Thanks in advance.
EDIT: Unfortunately, i can't share details of it, but i can share the general look of it. So i think this should be enough to have a picture of my problem.
from django.db import models
from django.contrib.auth.models import User
class TableF(models.Model):
information = models.CharField(max_length=256, null=False)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class TableE(models.Model):
information = models.CharField(max_length=256, null=False)
tableF = models.ForeignKey(TableF, on_delete=models.CASCADE)
class TableC(models.Model):
information = models.CharField(max_length=256, null=False)
tableEs = models.ManyToManyField(TableE, through='TableD')
class TableD(models.Model):
information = models.CharField(max_length=256, null=False)
tableC = models.ForeignKey(TableC, on_delete=models.CASCADE)
tableE = models.ForeignKey(TableE, on_delete=models.CASCADE)
class TableA(models.Model):
information = models.CharField(max_length=256, null=False)
tableCs = models.ManyToManyField(TableC, through='TableB')
class TableB(models.Model):
information = models.CharField(max_length=256, null=False)
tableA = models.ForeignKey(TableA, on_delete=models.CASCADE)
tableC = models.ForeignKey(TableC, on_delete=models.CASCADE)
class Recommendation(models.Model):
information = models.CharField(max_length=256, null=False)
tableA = models.ForeignKey(TableA, on_delete=models.CASCADE)
you can use a middleware to include de user to the thread locals and catch this user from get_queryset in the model manager.
from threading import local
_thread_locals = local()
def get_current_user():
return getattr(_thread_locals, 'user', None)
class ThreadLocals(object):
#staticmethod
def process_request(request):
_thread_locals.user = getattr(request, 'user', None)
in the settings
MIDDLEWARE = [
...
'path.to.file.ThreadLocals',
]
from your.path import get_current_user
class TableFManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(creator=get_current_user())
class TableF(models.Model):
information = models.CharField(max_length=256, null=False)
user = models.ForeignKey(User, on_delete=models.CASCADE)
objects = TableFManager()
another less invasive option could be to rewrite the get_queryset in the admin class. there you already have the user in the request
def get_rec_user(user):
tes = TableE.objects.filter(tableF__in=TableF.objects.filter(user=user))
aes = TableB.objects.filter(tableE__in=tes).values_list('tableA_id', flat=True)
return Recommendation.objects.filter(
tableA__in=TableA.objects.filter(id__in=aes)
)

Generating Django sitemap.xml: How to fix 'get_absolute_url' error

I have a “BigPage” model that has an element called “pagename” that has unique set to True. I would like to use the django-sitemaps framework to generate, populate, and continuously update the sitemap.xml file every time a new BigPage model with a new “pagename” element is created by adding the URL myapp.com/pagename to my project’s sitemap.xml file. Here's my BigPage model:
class BigPage(models.Model):
Pagename = models.CharField(max_length=128, blank=True, unique=True, null=True) #they will enter this input into a form field to reserve their unique url at myapp.com/pagename
PageNameOwner = models.CharField(max_length=128, blank=True, null=True) #owner of page enters their name
OwnerGender = models.CharField(max_length=7, choices=(('male', 'Male'), ('female', 'Female')), blank=True, null=True)
PageViewsCounter = models.IntegerField(null=False, default=0)
PageIsRemoved = models.BooleanField(default=False) #true if mods take down a person’s page
def __unicode__(self):
return self.Pagename
I have created the below sitemap.py file and placed it in the folder of my app where the BigPage model resides:
class BigPageSitemap(Sitemap):
changefreq = 'daily'
priority = 0.5
def items(self):
return BigPage.objects.all()
Then, in the main project url.py file (not the app url.py file) I have added this:
sitemaps = {
'Name of Page':BigPageSitemap
}
To the urlpatterns element this:
url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
My app url.py has the following url pattern where if a term is entered in the URL field that matches a pagename element that can then be queried to retrieve a BigPage object then it loads that page, but if the entered URL is not equal to a pagename element, it will give the user a 404:
url(r'^(?P<url_param>[a-zA-Z0-9_.-]*)/$', views.view_for_all_BigPages, name='view_for_all_BigPages'),)
After all of this, no sitemap file seems to generate if I check the app folder or main project folder. If I go to myapp.com/sitemap.xml I get the following error:
'BigPage' object has no attribute 'get_absolute_url'
What things have I done wrong? I really appreciate any help. I’ve been trying for days.
from the docs:
There is no location method in this example, but you can provide it in
order to specify the URL for your object. By default, location() calls
get_absolute_url() on each object and returns the result.
you didnot define location() method in your Sitemap class, thats why it is trying to call get_absolute_url() on your model. so you need to define get_absolute_url() in your model like this:
class BigPage(models.Model):
Pagename = models.CharField(max_length=128, blank=True, unique=True, null=True)
# ....
def __unicode__(self):
return self.Pagename
def get_absolute_url(self):
return reverse('view_for_all_BigPages', kwargs={'url_param': self.Pagename})
by the way, model attributes are written in small letters, pagename rather than Pagename.

Django 1.5: Display foreignkey values in both admin models

I'm not sure that my title really made sense. Basically (from the code below), when I access the admin screen, I want a project to display with its client and a client to display all attached projects. Is there any way to do this?
class Client(models.Model):
title = models.CharField(max_length=250, null=True)
#project = models.ManyToManyField(Project)
#status = models.CharField(max_length=250)
class Project(models.Model):
project_choices = (
('L1', 'Lead'),
('C1', 'Confirmed'),
('P1', 'In Progress'),
('P1', 'Paid'),
)
title = models.CharField(verbose_name='Project Title', max_length=250, null=True)
client = models.ForeignKey(Client)
project_status = models.CharField(max_length=2,
choices=project_choices,
default='P1')
def __unicode__(self):
return self.title
I would suggest setting up a custom ModelAdmin and using the list_display to indicate which fields you want to show in the admin. It is fairly customizable, and you can add callables that could display exactly the info you indicate. An example ModelAdmin for the Client Model is below.
# project/app/admin.py
# Callable to add to ModelAdmin List Display
def show_client_projects(obj):
project_list = [p.title for p in obj.project_set.all()]
return ', '.join(project_list)
show_client_projects.short_description = 'Client Projects'
# Custom ModelAdmin
class ClientAdmin(admin.ModelAdmin):
list_display = ('title', 'show_client_projects')
You will need to create ModelAdmin classes for your models to define columns to display in the built-in Django admin:
https://docs.djangoproject.com/en/dev/ref/contrib/admin/
This is particularly relevant:
ManyToManyField fields aren’t supported, because that would entail executing a separate SQL statement for each row in the table. If you want to do this nonetheless, give your model a custom method, and add that method’s name to list_display. (See below for more on custom methods in list_display.)
So, you can create a method for your Client which loads up the Client's projects, and include that in the list_display.
Something like this should put you on the right track:
# In your models.py...
from django.contrib import admin
class Client(models.Model):
title = models.CharField(max_length=250, null=True)
def projects(self):
return Project.objects.filter(client=self)
class ClientAdmin(models.ModelAdmin):
list_display = ('title','projects',)
admin.site.register(Client,ClientAdmin)

Django - Limit users who view the items

My Models:
class PromoNotification(models.Model):
title = models.CharField(_('Title'), max_length=200)
content = models.TextField(_('Content'))
users = models.ManyToManyField(User, blank=True, null=True)
groups = models.ManyToManyField(Group, blank=True, null=True)
I want to publish there items to templates with some permissions. The template is only show the notifications for users who are in list (users or/and group). What should I do? Thank you for any help. Please show me some codes if you can.
You might use a custom manager, which makes it easier to do this user filtering in multiple views.
class PromoNotificationManager(models.Manager):
def get_for_user(self, user)
"""Retrieve the notifications that are visible to the specified user"""
# untested, but should be close to what you need
notifications = super(PromoNotificationManager, self).get_query_set()
user_filter = Q(groups__in=user.groups.all())
group_filter = Q(users__in=user.groups.all())
return notifications.filter(user_filter | group_filter)
Hook up the manager to your PromoNotification model:
class PromoNotification(models.Model):
...
objects = PromoNotificationManager()
Then in your view:
def some_view(self):
user_notifications = PromoNotification.objects.get_for_user(request.user)
You can read more about custom managers in the docs: http://www.djangoproject.com/documentation/models/custom_managers/

Categories