Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 217
AbeEstrada Avatar asked Oct 01 '08 22:10

AbeEstrada


3 Answers

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})
like image 161
Anentropic Avatar answered Oct 19 '22 21:10

Anentropic


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.

like image 23
ilvar Avatar answered Oct 19 '22 21:10

ilvar


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 %}
like image 1
Stephen G Tuggy Avatar answered Oct 19 '22 21:10

Stephen G Tuggy