Django model select choice, populated with fields from other model instances - python

Suppose I have a model Car that has a field brand and a model owner that has two field: name and car_brand, whereas the latter should be one of the Car instances brands. I need to have a form where the user is presented with a text field name and a drop down select choice populated with all the brand name of Car instances. How would I achieve that?
Here is the code I am starting with. Feel free to correct. Thanks.
models.py
from django.db import models
class Car(models.Model):
brand = models.CharField(max_length=20)
class Owner(models.Model):
name = models.CharField(max_length=20)
car_brand = models.ForeignKey(Car)
forms.py
from django.forms import ModelForm, TextInput, Select
from app.models import Owner
class OwnerForm(ModelForm):
class Meta():
model = Owner
fields = ("name", "car_brand")
widgets = {
"name" : TextInput(attrs={"class" : "name"}),
"car_brand" : Select(attrs={"class" : "car_brand"}),
}

You could probably just define a __unicode__ method on your Car model and I think it should work fine. As Daniel mentioned, the form might be getting confused since you've overridden the widgets. I could be wrong, but I thought django automatically rendered attributes on form elements that can be used for styling purposes. Maybe you don't need to override the widgets. If not, you can specify the form field explicitly:
class OwnerForm(ModelForm):
car_brand = forms.ModelChoiceField(
queryset=Car.objects.all(),
widget=Select(attrs={'class': 'car_brand'}),
)
class Meta:
model = Owner
fields = ('name', 'car_brand')
widgets = {
'name': TextInput(attrs={'class': 'name'})
}
As a side note, is there any reason you don't have a CarBrand model and a foreign key field relating the Car model to it? That would be a more normalized approach to modeling your data.

Related

Setting relationship with chosen model class in Django Admin interface

Problem:
How to add relationship from chosen model instance to any other Django model dynamically via Django Admin interface?
Description:
I want to create Categories via Django Admin interface. Each Category has multiple Choices assigned to it. Choice(s) from given Category may be assigned only to objects of another specific Django class (model). Let's present a pseudocode example:
class Category(models.Model):
category_name = models.CharField()
class Choice(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="choices")
choice_name = models.CharField()
class ModelWithChoosableFields(models.Model):
possible_categories = ... # objects of class Category
selected_choices = ... # objects of class Choice
class Meta:
abstract = True
class Book(ModelWithChoosableFields):
...
class Animal(ModelWithChoosableFields):
...
Category with category_name = 'Genre' has three possible
Choices: choice_name = 'biography', choice_name = 'thriller'
and choice_name = 'poetry'.
Category with category_name = 'Animal type' has two possible
Choices: choice_name = 'mammal' and choice_name = 'reptile'.
Class Book may have one of the Choices from Category
category_name = 'Genre' assigned. However, Choices related to
category_name = 'Animal type' cannot be assigned to class Book.
Analogically, class Animal can only have Choices related to
category_name = 'Animal type' assigned to it.
In other words, in Admin panel for instance of class Book I want to have a way of selecting Choice objects from Categories appropriate for Book class.
The reason I want to do this is so that user using Django Admin interface can add dynamically possible Categories to chosen models (e.g. add Category category_name = "Conservation status" choosable for class Animal), add more Choices to Categories if needed (e.g. add another choice_name = 'fish' to category_name = 'Animal type'. This way it is very flexible for end admin user, no need to change anything in code.
I tried achieving it with Generic Relations - however, it wasn't successful, because AFAIK generic relation ties given object (e.g. Category) to instance of object of any other class, not generally to any other class (so, when using Generic Relations, for Category I would have to specify relationship with given Book object instance, not with Book class in general).
I wonder if such action is even feasible - I searched a lot and couldn't find anything. Maybe there is a simpler way? Thank you in advance!
With ContentTypes you can relate model instances to entire model classes, no overwriting necessary to achieve your goal.
Heres how to do it:
In your Category model define a many-to-many relationship to ContentType. This way, in your Category model-forms you will be able to choose which models this category applies to and you will be able to filter Choices based on whether their category contains a particular model. Use the limit_choices_to parameter of the ManyToManyField to restrict the ContentType choices to those with the correct app_label and of course exclude the Choice and Category models.
From the Book/Animal/Etc. models add many-to-many relationships to the Choice model and use the limit_choices_to parameter to limit the Choices to only those with a category which is related to the respective model.
Your models should then look somewhat like this:
from django.db import models
def get_ContentTypes():
appQ = models.Q(app_label='YourAppName') #change value of app_label to match your app's name
modelIsCatQ = models.Q(model='category')
modelIsChoice = models.Q(model='choice')
return appQ & ~modelIsCatQ & ~modelIsChoice
class Category(models.Model):
category_name = models.CharField(max_length=128)
asigned_models = models.ManyToManyField(ContentType,limit_choices_to=get_ContentTypes)
class Choice(models.Model):
choice_name = models.CharField(max_length=128)
category = models.ForeignKey(Category,on_delete=models.Model)
class Animal(models.Model):
choices = models.ManyToManyField(Choice,limit_choices_to=models.Q(category_assigned_models__model__startswith='animal'))
class Book(models.Model):
choices = models.ManyToManyField(Choice,limit_choices_to=models.Q(category_assigned_models__model__startswith='book'))
Aaand Voila. There you have it:
When creating/editing a category, you choose which models it should apply to
When creating/editing a Book/Animal/etc. you can only see the relevant choices.

django rest framework - foreign key fields are read only

The model:
class Item(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
item_num = models.IntegerField()
# other fields...
class Meta:
unique_together = [('company', 'item_num') ]
Serializer:
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('company_id', 'item_num', )
The problem is that django rest framework generates a ReadOnlyField() for the company_id field, so this field is not editable when I create a new instance in view code like this:
s = ItemSerializer(data=request.POST)
s.save()
I also lose the default UniqueTogetherValidator that is defined in the model.
Though, if I change the serializer field name from 'company_id' to 'company', I do get the validator, as drf will generate PrimaryKeyRelatedField so it will be editable.
How can I still name my foreign key objects like 'company_id', because I do prefer naming them like this, and still get the default validation and saving behavior? Preferably without adding to much code to the serializer.
company_id is a read-only field because it lacks a proper definition in the Model for the serializer to understand it.
Steps to get it working:
Add an explicit field definition
Add the constraint
Serializer would be:
class ItemSerializer(serializers.ModelSerializer):
company_id = serializers.PrimaryKeyRelatedField(source=company, queryset=Company.objects.all())
class Meta:
model = Item
fields = ('company_id', 'item_num', )
validators = [
UniqueTogetherValidator(
queryset=Item.objects.all(),
fields=('company_id', 'item_num')
)
]

Trying to extend AbstractUser to create multiple user types in Django

So I have been searching all around the internet for a full example of how to user AbstractUser when u have at least 2 different models. Didn't find anything conclusive.. at least that would work on latest version of Django (2.0.1).
I have 2 models, teacher and student, and registration needs to be different. Besides username, email, name and surname, I need for example, for the student, to upload a profile picture, email, phone, student_ID. And for teacher, bio, academic title and website. Did I start good ? What is the right approach ?
class Profile(AbstractUser):
photo = models.ImageField(upload_to='students_images')
email = models.EmailField()
phone = models.CharField(max_length=15, )
class Student(Profile):
student_ID = models.CharField(unique=True, max_length=14,
validators=[RegexValidator(regex='^.{14}$',
message='The ID needs to be 14 characters long.')])
def __str__(self):
return self.name
class Teacher(Profile):
academic_title = models.CharField(max_length=30)
bio = models.TextField()
website = models.URLField(help_text="E.g.: https://www.example.com", blank=True)
Your goals can be accomplished using a 'Profile' pattern. You don't necessarily need to use a custom user model for this. But you need to have a single common model to for authentication; you can use the builtin django user for this or a custom class... Your Student and Teacher models should be OnetoOne relationships. This is the recommended solution per the documentation.
If you wish to store information related to User, you can use a OneToOneField to a model containing the fields for additional information. This one-to-one model is often called a profile model, as it might store non-auth related information about a site user.
In your case, you may do something like this:
class StudentProfile(models.Model):
user = models.OneToOneField('User', related_name='student_profile')
# additional fields for students
class TeacherProfile(models.Model):
user = models.OneToOneField('User', related_name='teacher_profile')
# additional fields for teachers
Then you can create your registration forms based on these profile models.
class StudentResistrationForm(forms.ModelForm):
class Meta:
model = StudentProfile
fields = (...)
class TeacherRegistrationForm(forms.ModelForm):
class Meta:
model = TeacherProfile
fields = (...)
You can create the user instance to which the profile is related to at the same time you create the profile. You might do this with formsets, for example.
add
class Meta:
abstract = True
to profile model
and change AbstractUser to models.Model

I have a django model but i only want to put some of my models as forms.

For example, lets say this is my model:
class Author(models.Model):
author = models.CharField()
friends = models.CharField()
and I only want to have the friends as a form field
Are you using a ModelForm? If so, you set the fields you want using the fields variable in the Meta class:
from django import forms
from yourapp.models import Author
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
fields = [ "friends", ]
Note: The fields variable is a list, so make sure you include the trailing comma if you're only passing a single field.
Suggest that you have a read of the Django ModelForm Docs as well, as that way you'll get a bit ahead on all the other questions you're probably about to come up with :)

Set Django ModelForm visible fields at runtime?

I have a Django model:
class Customer(models.Model):
first_name=models.CharField(max_length=20,null=True, blank=True)
last_name=models.CharField(max_length=25,null=True, blank=True)
address=models.CharField(max_length=60,null=True, blank=True)
address2=models.CharField(max_length=60,null=True, blank=True)
city=models.CharField(max_length=40,null=True, blank=True)
state=models.CharField(max_length=2,null=True, blank=True)
From there, I created a ModelForm:
class CustomerForm(forms.ModelForm):
class Meta:
model=Customer
I'd like to be able to show pieces of the form in my template corresponding to specific information the users can change. For example, if I want to let the customers change their name, I'd like to be able to show a form that only has the fields 'first_name' and 'last_name'.
One way to do this would be to create a ModelForm for each of the various field snippets... for the name example, it would look something like:
class CustomerFormName(forms.ModelForm):
class Meta:
model=Customer
fields=('first_name','last_name')
This seems pretty inelegant, and inflexible. What I'd like to do is be able to specify the fields at runtime, so when I pass the dictionary from the view to the template, I can just set which fields I'd like to show. How can I set it up so that I set the fields for a form at runtime? I'd ideally like the final dictionary passed to look something like this:
{'name_change_form':CustomerFormName(<form with only first_name and last_name>), 'address_change_form':CustomerFormName(<form with only address fields>)}
Then, I know that whenever I output name_change_form.as_p, it'll have exactly the form fields that I'm looking for.
Thoughts? Also feel free to recommend a better way to do it.
from django.forms import ModelForm
from wherever import Customer
def formClassFactory(model,fields):
ff = fields
mm = model
class formClass(ModelForm):
class Meta:
model = mm
fields = ff
return formClass
form_class = formClassFactory( ('first_name','last_name') )

Categories