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.
Related
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']
I made some models which have ManyToMany, OneToMany relationships, and then I tried to make appropriate class in views.py, so that one can see sub models related to the chosen model.
But in terms of connecting models-serializers-views-urls, I just couldn't figure out how to make it work...
So, what I want to do is : (simplified)
There are 3 models.
Party
People
Food
So Party has ManyToMany relationship with People, and OneToMany relationship with Food. When I reached url like /party_id/people_id, then I want to get specific person's information from given party id.
Here goes my code.
models.py
class Party(models.Model):
par_id = models.TextField()
par_people = models.ManyToManyField(People)
class People(models.Model):
peo_id = models.TextField()
peo_name = models.TextField()
peo_type = models.TextField()
class Food(models.Model):
foo_id = models.TextField()
foo_party = models.ForeignKey(Party, on_delete=models.CASCADE)
serializers.py
class PartySerializer(serializers.ModelSerializer):
class Meta:
model = Party
fields = ('par_id', 'par_people')
# People, Food has same structure...
views.py
class PartyList(generics.ListAPIView):
queryset = Party.objects.all()
serializer_class = PartySerializer
# People, Food has same structure...
urls.py
Here's the part where I got lost
#redundancy reduced...(e.g. import)
urlpatterns = [
path('party/<int:par_id>/<int:peo_id>', views.PartyList.as_view()),
path('party/<int:par_id>/<int:foo_id>', views.PartyList.as_view()),
]
So If I reach website/party/1/3, I want to see person's information(whose peo_id is 3) of party(whose par_id is 1). For food, It goes the same.
Should I make new class in views.py to make it work? But how can url check par_id and foo_id at the same time if I use PartyList view class..? Any help would be much appreciated.
I think something like this should work. The basic principle if work out if using peo_id or foo_id and then filter the queryset on that basis.
def get (self, *args, **kwargs):
id = kwargs.get(peo_id, None)
if id:
self.queryset.filter(par_people__peo_id=id)
else:
id = kwargs.get(foo_id, None)
self.queryset.filter(foo_party=id)
My class looks like this:
class Foo(models.Model):
known_by = JSONField()
My data looks like this
{ "known_by" : [
{'by':'name1', 'value':['def']},
{'by':'name2', 'value':['bar']}
]
}
Is there any way for me to enforce that the Jsonfield needs to follow the format of by,value[] dict. I know how to do this using serialiazers
Any other cleaner way to enforce this(in the model definition itself)? Thanks
You can add a validator to the model field, like this:
class Foo(models.Model):
known_by = ArrayField(JSONField(max_length=100), size=4, validators=[a_json_array_validator])
And the validator is:
def a_json_array_validator(value):
if any([not is_json_valid(entry) for entry in value]):
raise ValidationError(
_('%(value) is not a valid json'),
params={'value': value},
)
(The actual json validation is up to you) ;-)
Note that validators receive python objects so its actually a dict.
You could implement it this way:
from django.db import models
class Bar(models.Model):
by = models.CharField()
value = models.ArrayField()
class Foo(models.Model):
known_by = models.ForeignKey(Bar, on_delete=models.CASCADE)
Why not just override the save method to do the enforcement?
class Foo(models.Model):
known_by = JSONField()
def save(self, *args, **kwargs):
# begin validation code
# end validation code
if valid:
super(Model, self).save(*args, **kwargs)
else:
# something else, maybe http error 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 two models related by a foreign key:
# models.py
class TestSource(models.Model):
name = models.CharField(max_length=100)
class TestModel(models.Model):
name = models.CharField(max_length=100)
attribution = models.ForeignKey(TestSource, null=True)
By default, a django ModelForm will present this as a <select> with <option>s; however I would prefer that this function as a free form input, <input type="text"/>, and behind the scenes get or create the necessary TestSource object and then relate it to the TestModel object.
I have tried to define a custom ModelForm and Field to accomplish this:
# forms.py
class TestField(forms.TextInput):
def to_python(self, value):
return TestSource.objects.get_or_create(name=value)
class TestForm(ModelForm):
class Meta:
model=TestModel
widgets = {
'attribution' : TestField(attrs={'maxlength':'100'}),
}
Unfortunately, I am getting: invalid literal for int() with base 10: 'test3' when attempting to check is_valid on the submitted form. Where am I going wrong? Is their and easier way to accomplish this?
Something like this should work:
class TestForm(ModelForm):
attribution = forms.CharField(max_length=100)
def save(self, commit=True):
attribution_name = self.cleaned_data['attribution']
attribution = TestSource.objects.get_or_create(name=attribution_name)[0] # returns (instance, <created?-boolean>)
self.instance.attribution = attribution
return super(TestForm, self).save(commit)
class Meta:
model=TestModel
exclude = ('attribution')
There are a few problems here.
Firstly, you have defined a field, not a widget, so you can't use it in the widgets dictionary. You'll need to override the field declaration at the top level of the form.
Secondly get_or_create returns two values: the object retrieved or created, and a boolean to show whether or not it was created. You really just want to return the first of those values from your to_python method.
I'm not sure if either of those caused your actual error though. You need to post the actual traceback for us to be sure.
TestForm.attribution expects int value - key to TestSource model.
Maybe this version of the model will be more convenient for you:
class TestSource(models.Model):
name = models.CharField(max_length=100, primary_key=True)
Taken from:
How to make a modelform editable foreign key field in a django template?
class CompanyForm(forms.ModelForm):
s_address = forms.CharField(label='Address', max_length=500, required=False)
def __init__(self, *args, **kwargs):
super(CompanyForm, self).__init__(*args, **kwargs)
try:
self.fields['s_address'].initial = self.instance.address.address1
except ObjectDoesNotExist:
self.fields['s_address'].initial = 'looks like no instance was passed in'
def save(self, commit=True):
model = super(CompanyForm, self).save(commit=False)
saddr = self.cleaned_data['s_address']
if saddr:
if model.address:
model.address.address1 = saddr
model.address.save()
else:
model.address = Address.objects.create(address1=saddr)
# or you can try to look for appropriate address in Address table first
# try:
# model.address = Address.objects.get(address1=saddr)
# except Address.DoesNotExist:
# model.address = Address.objects.create(address1=saddr)
if commit:
model.save()
return model
class Meta:
exclude = ('address',) # exclude form own address field
This version sets the initial data of the s_address field as the FK from self, during init , that way, if you pass an instance to the form it will load the FK in your char-field - I added a try and except to avoid an ObjectDoesNotExist error so that it worked with or without data being passed to the form.
Although, I would love to know if there is a simpler built in Django override.