In my app, I've got 2 types of users: Student and Teacher. I want to store additonal information about each of them in separate table.
class Student(models.Model):
user = models.ForeignKey(User)
nr_indeksu = models.BigIntegerField()
def __unicode__(self):
return unicode(self.user)
Let's assume i want to register user as student - In that case, i need to expand User registration form, so it will contain fields from Group and Student model, and during registration i want to insert Student.nr_indeksu with Student.user_id(FK) into student table, and Group.name into User_Groups.
I've created StudentForm by using Meta Class and rendered it in template in the same form where UserCreationForm is, but i don't know how to handle saving into database, because UserCreationForm creates user_id that it's supposed to go into Student mode. Any pointers?
I had a similar issue. Solved it by making the model look a little different:
class Student(User):
nr_indeksu = models.BigIntegerField()
def __unicode__(self):
return unicode(self.user)
This way, when you define the Django form, all needed fields will be rendered for this model. This is because you are saying The student is a user , as opossed to the former the student has a user
The form could look like:
class StudentForm(forms.ModelForm):
class Meta:
model = Student
Edit:
Use a similar approach for teacher model.
If you want to avoid the rendering of some fields, use the fields attribute in the Meta class.
Second Edit:
If you want to preserve the validations and checks of the User form, the form should also inherit from UserCreationForm.
Related
For context, I'm trying to create a form that allows users to upload info about their own custom Pokemon. Basically, they are creatures that you can catch, name, and level up. To draw a comparison, it is a similar concept to dogs; there are labradors, German Shepherds, huskies, etc. that would be variations of a base Dog model, but then each individual would have a name and other defining characteristics.
I've created Pokemon and CustomPokemon models and imported the latter into my forms.py file. I'm trying to access some parent fields but am unable to:
from django import forms
from .models import CustomPokemon
class PokemonForm(forms.ModelForm):
class Meta:
model = CustomPokemon
fields = ['pokemon.poke_name', 'name', 'level']
The poke_name field is inherited from the parent Pokemon model while the other two fields belong to the CustomPokemon model. I'm getting this FieldError:
Unknown field(s) (pokemon.poke_name) specified for CustomPokemon.
The issue isn't resolved by using poke_name, so I'm curious how I can access the parent model's fields so they can be displayed in the form.
First option
If you just want a dropdown displaying the field poke_name, what you could do is to define a __str__ method inside Pokemon model like this:
class Pokemon(model.Model):
...
def __str__(self):
return self.poke_name
Then, you can define the form as follows:
class PokemonForm(forms.ModelForm):
class Meta:
model = CustomPokemon
fields = ['pokemon', 'name', 'level']
And you will get a dropdown displaying all the poke_name of your database, where you can choose your foreign key. It would be better if poke_name is a unique field so that the foreign key can be clearly identified.
Second option
If you need more freedom, you could manually define a custom field as follows:
class PokemonForm(forms.ModelForm):
poke_name = forms.CharField()
class Meta:
model = CustomPokemon
fields = ['name', 'level']
Then, when validating the form, you should take care of whether the entry exists and/or create it:
if form.is_valid():
form.instance.pokemon = Pokemon.objects.get_or_create(cname=form.cleaned_data['poke_name'])
form.save()
I have built an Angular form that contains a form array. So the user has the control to add fields or delete fields from the form. What I am trying to understand is how I can design a Django API that can handle the post for this kind of dynamic form?
I do not want the user to create an account on the website in order to place his order. Is that even possible?
You should be more concerned about how to model your data, than you can think about your interface. Here a few options for modeling your data:
Option One is to use regular Django ORM, and in this case you may use the JSONField for any dynamic properties.
Option two is to use any schemaless data model, such as document-based databases(e.g MongoDB).
Here a simple example, on how to use Django's JSONField:
your model:
class Order(models.Model):
customer = models.ForeignKey(User, on_delete=models.CASCADE)
#any additional static fields
properties = JSONField()
your view:
def create_order_view(request):
if request.method == "POST":
#do your validation
Order.objects.create(user=request.user, properties=request.POST["properties"])
return Response(status=200)
this example is totally incomplete as you have to add validation error handling, and it is a better idea to use Django rest-framework for constructing your API.
Finally as I said there many option to model your data, in addition to what I mentioned above there are many other. To decide what model to use, you have to know how your data gonna be consumed, so you can optimze for query time, in addition there are many other factors but this is out of scope of this asnwer.
For me, I used Django-RESTframework to build the api.
The way to achieve this is simple, just create the model and iterate through the items which is the dynamic part, and assign the Foreignkey field to obj.id created. First, I created the main model instance, then created the instances of the child instances. I will use Order and Item to demonstrate the idea, The Item instance will have Foreinkey field to Order model.
In the Item model, add "related_name" argument to the Foreinkey field
order = models.ForeignKey(Order, related_name='items',on_delete=models.CASCADE)
serializers.py
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = [
....your fields...
]
class OrderSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True)
class Meta:
model = Order
fields = [
'order', ....
]
def create(self, validated_data):
items_data = validated_data.pop("items")
order = Order.objects.create(**validated_data)
order.total_fees = order.delivery_fees
for item in items_data:
i = Item.objects.create(order=order, **item)
return order
Say I have a model called MyUser. It has some field, and one of them is this one:
user = OneToOneField(User, related_name='more_user_information')
I want to make a view to update this model, and I do the following:
Class AccountEdit(LoginRequiredMixin, UpdateView):
model = MyUser
form_class = MyUserForm
template_name = 'accounts/edit.html'
def get_object(self, queryset=None):
return self.model.objects.get(user=self.request.user)
Each field in MyUser renders fine for editing, except user. This one to one field becomse a select drop down box. What I like to do is to edit the fields on User model like first name or last name.
How can I achieve this while extending UpdateView? or perhaps shuold I use a FormView?
thanks
This problem is actually nothing to do with class based views or update view - its a basic issue that has been there since the beginning, which is:
ModelForms only edit the fields for one model, and don't recurse into
foreign keys.
In other words, if you have a model like this:
class MyModel(models.Model):
a = models.ForeignKey('Foo')
b = models.ForeignKey('Bar')
c = models.ForeignKey('Zoo')
name = models.CharField(max_length=200)
A model form will render three select fields, one for each foreign key, and these select fields will have all the values from those models listed - along with one text field for the name.
To solve this problem, you need to use InlineFormSets:
Inline formsets is a small abstraction layer on top of model formsets.
These simplify the case of working with related objects via a foreign
key.
You should use InlineFormSet from the excellent django-extra-views app. To do this, you'll create a view for the related object as well:
class MyUserInline(InlineFormSet):
model = MyUser
def get_object(self):
return MyUser.objects.get(user=self.request.user)
class AccountEditView(UpdateWithInlinesView):
model = User
inlines = [MyUserInline]
Another option is django-betterforms's Multiform and ModelMultiForm.
Example:
class UserProfileMultiForm(MultiForm):
form_classes = {
'user': UserForm,
'profile': ProfileForm,
}
It works with generic CBV (CreateView, UpdateView, WizardView).
I altered the User class in "/home/david/django/django/contrib/auth/models.py" as follows to override the string representation for a user in my Django application.
class User(models.Model):
...
def __unicode__(self):
return self.get_profile().full_name()
I had written a function called full_name() in my user profile model to display full names the way I want them to be displayed.
However, after I restart Apache, I find that users in select menus of model forms are still represented by usernames. Why?
Don't try to monkey patch you installation. It really is a bad idea.
You can read here how you can override the way a model choice form field shows its model instances.
In you case it would look something like this:
class UserChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.get_profile().full_name()
Then use this field in your forms. In a model form you will have to override the default field that is used.
I am not sure if my language clear enough but basically I have this form:
class Paper(models.Model):
number = models.CharField(max_length=12,primary_key=True)
project = models.ForeignKey(Project)
class SomeForm(forms.ModelForm):
class Meta:
model = Paper
fields = ('project', 'number')
and django creates a textfield for me. What I want is a select box with the existing primary keys.
Thanks.
I don't think you want a modelform - that's for creating and updating model instances. It sounds like you just want a form to select IDs. Use a normal form, but with a ModelChoiceField.
class SomeForm(forms.Form):
ids = forms.ModelChoiceField(queryset=Paper.objects.all())
You'll need to give the Paper model a __unicode__ method that returns self.number.