Consider my models.py,
PowerPolicy:
class PowerPolicy(models.Model):
name = models.CharField(max_length=15)
...
Group:
class Group(models.Model):
name = models.CharField(max_length=15)
#But then, we also have:
power_policies = models.ManytoManyField(PowerPolicy)
Player:
class Player(models.Model):
name = models.CharField(max_length=15)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
...
And then another model called,
UsePower:
class UserPower(models.Model):
player = models.ForeignKey(Player, on_delete=models.CASCADE)
power_policy = models.ForeignKey(PowerPolicy, on_delete=models.CASCADE)
...
But! Here's the catch: I want to make it so that my superuser (Note that my superuser isn't a player, he's simply a superuser) can only create a UsePower object of the Powers specified in the Player's Group. Now, I do know that I have to create a custom form and override the queryset of the power_policy field that returns, my the custom queryset according to my needs through a function.
- Here's what it would look something like:
class UsePowerForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(UsePowerForm, self).__init__(*args, **kwargs)
def MyCustomFunctionThatReturnsTheQuerySet():
This function returns the Power policies that are allowed to the player in
their player Group. The only problem is,
Our little function here doesn't know how to get the player chosen.
could you help
return TheQuerySet
self.fields['power_policy'].queryset = MyCustomFunctionThatReturnsTheQuerySet()
And then use it on the Admin Site, by doing this:
class UsePowerAdmin(admin.ModelAdmin):
form = UsePowerForm
admin.site.register(UsePower, UsePowerForm)
I really hope this makes sense, and you guys could help me out.
Thank you for your time reading this, I honestly do appreciate it.
EDIT: Using form cleaning, or verifying during save, is not an option for me :(
You can get the player when the form is being initialized:
class UserPowerForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(UsePowerForm, self).__init__(*args, **kwargs)
player = Player.objects.get(id=self.initial['player'])
###from here you can use player to get the power policies and put into list
self.fields['power_policy'] = forms.ChoiceField(choices=power_policy_list)
class Meta:
model = UserPower
fields = ['player', 'power_policy']
Related
My friends and I play a spreadsheet-based sports picking game which is very tedious to make changes to. I've wanted to learn Django for a while so I've been working on creating it as a webapp. Here are the models I'm working with:
class Sheet(models.Model):
user = models.ForeignKey(User)
... other stuff
class Game(models.Model):
home_team = models.CharField(max_length=100, default='---')
away_team = models.CharField(max_length=100, default='---')
... other stuff
class Pick(models.Model):
sheet = models.ForeignKey(Sheet)
game = models.ForeignKey(Game)
HOME = 'H'
AWAY = 'A'
PICK_TEAM_CHOICES = (
(HOME, 'Home'),
(AWAY, 'Away'),
)
pick_team = models.CharField(max_length=4,
choices=PICK_TEAM_CHOICES,
default=HOME)
... other stuff
Right now, I'm trying to nail down a simple way to display the following form with information from a foreign keyed model instead of the pick_team default choices. The game is hidden because it's paired with the generated PickForm via use of the initial functionality in the view.
class PickForm(ModelForm):
class Meta:
model = Pick
widgets = {'game': forms.HiddenInput()}
fields = ['sheet','game','amount','pick_type','pick_team']
def __init__(self, *args, **kwargs):
game = kwargs['initial']['game']
super(PickForm, self).__init__(*args, **kwargs)
self.fields['pick_team']=forms.ModelChoiceField([game.away_team,game.home_team])
From what I can tell, the ModelChoiceField expects a queryset- so when I provide a list or a tuple, I get a 'list' object has no attribute 'iterator' error. Knowing this now, how can I display the Game fields home_team and away_team in the pick_team dropdown on the template? Currently it defaults to 'Home' and 'Away'.
I know this is a common question at the core- how to display ForeignKeyed information in a dropdown, however all the answers I've found are fine with providing a queryset to ModelChoiceField, because they're typically trying to list a field from every object (or some filtered subset). In this case, I only want to list 2 fields, and only from one object.
I tried returning a queryset consisting of the Game object already present in kwargs, but it just displays the game's str() method in the dropdown, and attempting to refine the queryset with the relevant field names isn't working either.
EDIT: I realized that actually using the home_team and away_team values from the Game object would require extra processing on saving the Pick, or possibly be harder than that. Is there any way to do this sort of aliasing in the template alone? Similar to how with choice fields I can use get_pick_team_display to show a nicer looking display value ('Home', 'Away') instead of the vague 'H' or 'A'.
EDIT2: View code
class GameDetail(DetailView):
#model = Game
template_name = 'app/games.html'
context_object_name = 'game_detail'
def get_object(self):
game = get_object_or_404(...object filtering)
return game
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
try:
pick = Pick.objects.get(game=context['game_detail'],
....other stuff)
context['pickform'] = PickForm(initial={'game':context['game_detail'],
.... other stuff
except Pick.DoesNotExist:
#pick = none
context['pickform'] = PickForm(initial={'game':context['game_detail'],
})
return context
def post(self, request, *args, **kwargs):
form = PickForm(request.POST)
if form.is_valid():
....process form
This is a quickfix approach. In your __init__, instead of reassigning pick_team field, just redefine its options as follows:
self.fields['pick_team'].choices = (
('H', game.home_team),
('A', game.away_team),
)
My Models look like this:
class Car(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=200)
class Owner(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
car = models.ForeignKey(Car)
and a Form that looks like this:
class CarForm (forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CarForm, self).__init__(*args, **kwargs)
self.fields['car_name']=forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off'}),label='', required=False)
self.fields['person_name']=forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off'}),label='', required=False)
So when the user goes to my index file he find two forms, one for his name and one for the car's name which when submitted will be created in the database then.
So now I am in the shell and want to test that and I'm not sure what the correct syntax is, I've tried this:
response = client.post('/', {'car_name':'something','person_name':'something'})
but it always returns:
IndexError: list index out of range
What does that mean? Or what's the correct syntax to run the tests?
I've also tried this:
response = client.post('/', {'id_car_name':'something','id_first_name':'something'})
Since these are the ids that Django creates in the homepage, but it didn't work
Your test case syntax looks correct. The field names from your first example are correct and match those declared in self.fields. Here is a more detailed example (with assertions as desired):
from django.test import Client, TestCase
class CarFormTest(TestCase):
def test_form(self):
client = Client()
response = client.post('/', {'car_name':'something','person_name':'something'})
self.assertEqual(response.status_code, 200)
I don't see anything in your test line that would cause an IndexError.
Given that you did not map the Car model to the CarForm like this:
class CarForm(forms.modelForm):
class Meta:
model = Car
It appears that you don't want a direct mapping and instead you need a custom save() method on your CarForm to create Car and Owner objects when you save the form. The IndexError must be coming from a line of code that you have not shown.
Please update your question to provide additional code.
I have next models:
class CategoryLesson(models.Model):
title = models.CharField()
class Lesson(models.Model):
title = models.CharField()
category = models.ForeignKey(CategoryLesson)
class UserRole(models.Model):
rolename = models.CharField()
lessons = models.ForeignKey(CategoryLesson)
group = models.ForeignKey(Group)
class SiteUser(models.Model):
username = models.OneToOneField(User)
roles = models.ManyToManyField(UserRole)
There are 3 categories, and no limit users.
And i cant understand how limit access some SiteUser to some CategoryLesson & Lesson . Already watched django-guardian, and dont see some of the differences between this application and django generic has_perm for this case. Is really i should create 3 models for each category??
Here you have to set a check in GET request whether your user has permission to access the specific categorylesson. Lets assume that your url is like this: 192.168.0.1:8000/categorylessson/?cat_id=1. (I am using a class based view here.)
class CategoryLessonList(TemplateView):
...
def get(self, request, *args, **kwargs):
siteuser= SiteUser.objects.get(username=request.User)
siteuser_roles= siteuser.roles.all()
specific_category= CategoryLesson.objects.get(id= int(request.GET.get('cat_id')))
for role in siteuser_roles:
r_lessons=role.objects.filter(lessons= specific_category)
if len(r_lessons)>0:
return super(CategoryLessonList, self).get(request, *args, **kwargs)
return redirect('/no-access')
PS: its an untested code.
I have a model form, which I'm trying to pass a model instance to initialize values:
class ProjectModelForm(ModelForm):
class meta:
model = Project
def __init__(self, project=None, *args, **kwargs):
super(ProjectModelForm, self).__init__(*args, **kwargs)
if project:
self.fields['zipcode'].initial = project.zipcode
The problem is that the field seems to be populated with a tuple:
(u'90210',)
This happens even when I hardcode with a value I know to be an integer or string:
self.fields['zipcode'].initial = 90210 renders as (90210,).
self.fields['zipcode'].initial = '90210' renders as ('90210',).
Could someone explain what is happening here, and suggest the best route to rendering the result as a simple string?
Any help much appreciated.
EDIT
models.py:
class Project(models.Model):
...
zipcode = models.CharField(max_length=5, null=True, blank=True)
You can pass an initial dictionary of default values when initializing the form:
In View:
initial = {}
if project:
initial.update({'zipcode': project.zipcode})
form = ProjectModelForm(initial=initial)
I have a CarType that has a ForeignKey BodyMaterial in my models.py:
class BodyMaterial(models.Model):
location = models.ForeignKey('CarType')
name = models.CharField(max_length=255)
class CarType(models.Model):
name = models.CharField(max_length=255)
default_body_material = models.ForeignKey(BodyMaterial, null = True, blank = True, default = "", limit_choices_to={'location__exact': 1})
BodyMaterial is an Inline in CarType in my admin.py:
class BodyMaterial_Inline(admin.StackedInline):
model = BodyMaterial
extra = 1
class CarType_Admin(admin.ModelAdmin):
inlines = [BodyMaterial_Inline]
admin.site.register(CarType, CarType_Admin)
I would like to filter the ForeignKey for default_body_material to show only the relevant BodyMaterials (the ones that appear/added on the same admin page). For example, I created a 2 seat CarType and in the same page added some BodyMaterials. Then I create an SVU CarType and some other BodyMaterials. When I go back to the 2 seat CarType, I would like to see only the relevant BodyMaterials in the drop-down for default_body_material.
I try to filter using limit_choices_to on the id. So I'm doing this using post_init because the id for the object in determined in runtime:
def setID(**kwargs):
instance = kwargs.get('instance')
default_body_material = instance._meta.get_field_by_name('default_body_material')[0]
default_body_material.limit_choices_to = {'location__exact': instance.id}
post_init.connect(setID, CarType)
Unfortunately, that does nothing. What am I missing? Is there a beter why of filtering ForeignKey for my purposes (this is probably very basic)?
Note that this question is only for the admin interface.
Just use a custom ModelForm:
class CarTypeAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CarTypeAdminForm, self).__init__(*args, **kwargs)
# Can't limit it until instance has been saved at least once
if self.instance.pk:
self.fields['default_body_material'].queryset = \
self.fields['default_body_material'].queryset \
.filter(location=self.instance)
class CarTypeAdmin(admin.ModelAdmin):
form = CarTypeAdminForm
...
You want to look at overriding the queryset function for your inline.