How to get one field from foreign key in django template? - django

Hi, I have this models:
class Mercadoria(models.Model):
idmercadoria = models.IntegerField(verbose_name=u'Código', primary_key=True)
referencia = models.CharField(verbose_name=u'Referência', max_length=30)
descricao = models.CharField(verbose_name=u'Descrição', max_length=250)
status = models.CharField(verbose_name=u'Status', max_length=1)
class Meta:
ordering = ['referencia', 'descricao']
managed = False
db_table = 'mercadoria'
def __unicode__(self):
return self.referencia + ' - ' + self.descricao
class Produto(models.Model):
idproduto = models.IntegerField(verbose_name=u'Código', primary_key=True)
idmercadoria = models.ForeignKey('Mercadoria', db_column='idmercadoria',
verbose_name=u'Mercadoria')
idtamanho = models.ForeignKey('Tamanho', db_column='idtamanho',
verbose_name=u'Tamanho')
idcores = models.ForeignKey('Cores', db_column='idcores',
verbose_name=u'Cores')
estoqueatual = models.DecimalField(u'Estoque Atual', max_digits=18,
decimal_places=4, null=False, default=0)
saldodisponivel = models.DecimalField(u'Saldo Disponível', max_digits=18,
decimal_places=4, null=False, default=0)
codigobarra = models.CharField(verbose_name=u'Código Barras', max_length=13)
tipoproduto = models.CharField(verbose_name=u'Tipo Produto', max_length=1)
class Meta:
ordering = ['idmercadoria']
managed = False
db_table = 'produto'
class ItensPedido(models.Model):
idpedido = models.ForeignKey('Pedido', db_column='idpedido',
primary_key=True, default=-1)
idproduto = models.ForeignKey('Produto', db_column='idproduto')
And I have a problem to get "referencia" fields from "Mercadoria" model, using "ItensPedido" model.
The problem is: this database is legacy database, used by your ERP Delphi software, and the person who design the database, is crazy! In my template, I want to get "Mercadoria" models data.
I try this: {{ itens.idproduto.idmercadoria.descricao.value }} e {{ itens.idproduto__idmercadoria__descricao.value }} but doesn't work.
I try to modify my view to get select_related() work, like this:
def get_field_qs(field, **kwargs):
if field.name == 'idproduto':
field.queryset = Produto.objects.select_related()
return field.formfield(**kwargs)
ItensInlineFormSet = inlineformset_factory(Pedido, ItensPedido, form=PedidoItensForm,
extra=1, fk_name='idpedido', formfield_callback=get_field_qs)
and same problem.. doesn't show me the data from "mercadoria" model.
What`s I'm doing wrong?
Thanks

Related

Multiple forms with one single create view in Django

I have 5 forms: MyForm, EducationForm, ExperienceForm, RecommendationForm, OtherDocumentsForm
I want to disply them in one form template. I can not do it with CreateView because it only accepts one form class. How can I create single view for multiple forms?
class MyForm(forms.ModelForm):
class Meta:
model = UserForm_uz
fields = 'all'
class EducationForm(forms.ModelForm):
class Meta:
model = Education_uz
fields = 'all'
class ExperienceForm(forms.ModelForm):
class Meta:
model = Experience_uz
fields = 'all'
class RecommendationForm(forms.ModelForm):
class Meta:
model = Recommendation_uz
fields = 'all'
class OtherDocumentsForm(forms.ModelForm):
class Meta:
model = OtherDocuments
fields = 'all'
I want all the forms to be submitted in a single request and single button. They related with foreignkey to each other EducationForm, ExperienceForm, RecommendationForm, OtherDocumentsForm connected to MyForm with foreignKey
My models:
from django.db import models
language_choices = [('1', 'Билмайман'),
('2', 'Ёмон'),
('3', 'Лугат ёрдамида'),
('4', 'Ўртача'),
('5', 'Яхши'),
('6', 'Жуда яхши'), ]
approve_choices = [('Yes', 'Ха'),
('No', 'Йўк')]
agreement_choices = [('Yes', 'Ха'),
('No', 'Йўк')]
class UserForm_uz(models.Model):
rasm = models.ImageField(upload_to='rasmlar',null=True,blank=True)
lastName = models.CharField(max_length=200)
firstName = models.CharField(max_length=200)
middleName = models.CharField(max_length=200)
birthData = models.DateField()
nation = models.CharField(max_length=50)
birthPlace = models.CharField(max_length=250)
marriage_status = models.CharField(max_length=20)
children = models.CharField(max_length=20)
militaryResp = models.CharField(max_length=150)
language_uzbek = models.CharField(choices=language_choices,max_length=150)
language_russian = models.CharField(choices=language_choices,max_length=150)
language_english = models.CharField(choices=language_choices,max_length=150)
language_boshqa = models.CharField(max_length=50)
computer_literacy = models.CharField(max_length=15)
functional_resp = models.CharField(max_length=250)
work_experience = models.CharField(max_length=200)
yutuqlar = models.CharField(max_length=200)
leaving_work_reason = models.CharField(max_length=200)
main_skills = models.CharField(max_length=300)
expected_salary = models.CharField(max_length=100)
reasontoWork = models.CharField(max_length=300)
relatives_company = models.CharField(max_length=300)
criminal_history = models.CharField(max_length=250)
homeNumber = models.CharField(max_length=15)
phoneNumber = models.CharField(max_length=15)
email = models.EmailField()
additional_info = models.CharField(max_length=300)
approve_info = models.CharField(choices=approve_choices,max_length=20)
agreement = models.CharField(choices=agreement_choices,max_length=20)
passport_file = models.FileField(upload_to='fayllar')
diplom_file = models.FileField(upload_to='fayllar')
trudovoyKnishka = models.FileField(upload_to='fayllar')
fullName = models.CharField(max_length=100)
class Education_uz(models.Model):
form = models.ForeignKey(
UserForm_uz,
on_delete=models.CASCADE,
)
startingDate = models.DateField()
endingDate = models.DateField()
name = models.CharField(max_length=200)
degree = models.CharField(max_length=50)
speciality = models.CharField(max_length=150)
diplomSeriya = models.CharField(max_length=50)
class Experience_uz(models.Model):
form = models.ForeignKey(
UserForm_uz,
on_delete=models.CASCADE,
)
startWorkDate = models.DateField()
endWorkDate = models.DateField()
name = models.CharField(max_length=100)
lavozim = models.CharField(max_length=100)
address = models.CharField(max_length=100)
class Recommendation_uz(models.Model):
form = models.ForeignKey(
UserForm_uz,
on_delete=models.CASCADE,
)
fullName = models.CharField(max_length=150)
workPlace = models.CharField(max_length=150)
phoneAndEmail = models.CharField(max_length=100)
class OtherDocuments(models.Model):
form = models.ForeignKey(
UserForm_uz,
on_delete=models.CASCADE,
)
file = models.FileField(upload_to='fayllar')
comment = models.CharField(max_length=100)
Since MyForm will be submitted at the same time as the other forms you need to exclude the ForeignKey field to UserForm_uz from all the other models, the related object doesn't exist yet so you can't select it
class EducationForm(forms.ModelForm):
class Meta:
model = Education_uz
# Repeated for all four forms
exclude = ['form'] # Whatever the ForeignKey to UserForm_uz is named
Here's an example view that uses three of the forms (I missed out two to save typing). Give each form a prefix, this reduces the risk of having form fields with conflicting names. Validate them all in one go, if any form is invalid the view should not continue. Save MyForm first and use the output to pass to the other forms as the foreign key value
def my_view(request):
if request.method == 'POST':
my_form = MyForm(request.POST, request.FILES, prefix='user')
education_form = EducationForm(request.POST, request.FILES, prefix='education')
experience_form = ExperienceForm(request.POST, request.FILES, prefix='experience')
if all([my_form.is_valid(), education_form.is_valid(), experience_form.is_valid()]):
form = my_form.save()
education = education_form.save(commit=False)
education.form = form
education.save()
experience = experience_form.save(commit=False)
experience.form = form
experience.save()
return redirect('some-view')
else:
my_form = MyForm(prefix='user')
education_form = EducationForm(prefix='education')
experience_form = ExperienceForm(prefix='experience')
return render(request, 'template.html', {'my_form': my_form, 'education_form': education_form, 'experience_form': experience_form})
In your template (template.html) you'll need to render all forms in the same form tag
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ my_form }}
{{ education_form }}
{{ experience_form }}
<input type="submit" />
</form>

How to add nested field after creating the ModelSerializer? Not in the class

Basically i want to have fk_inventory as a nested field in StorageRackSerializer but as you guys can see I also need to use StorageRackSerializer in InventorySerializer.
How can i set the field after creating the serializer class?
I have tried creating a fk_inventory field and set it to None and tried to set to InventorySerializer afterwards but didn't work.
class Inventory(models.Model):
inventory_id = models.AutoField(primary_key=True)
fk_building = models.OneToOneField(Store, on_delete=models.CASCADE, unique=True, related_name='inventory')
def __str__(self):
return f"{self.inventory_id}"
class StorageRack(models.Model):
storage_rack_id = models.AutoField(primary_key=True)
quantity = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(50)])
fk_inventory = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name="storage_racks")
fk_product_id = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True, related_name="storage_racks")
def __str__(self):
return f"{self.storage_rack_id}"
class StorageRackSerializer(serializers.ModelSerializer):
fk_product_id = ProductSerializer(read_only=True)
fk_inventory = None
class Meta:
model = StorageRack
fields = ('storage_rack_id', 'quantity', 'fk_inventory', 'fk_product_id')
class InventorySerializer(serializers.ModelSerializer):
fk_building = StoreSerializer()
storage_racks = StorageRackSerializer(many=True)
class Meta:
model = Inventory
fields = ('inventory_id', 'fk_building', 'storage_racks')
StorageRackSerializer.fk_inventory = InventorySerializer()
You can add a field 'fk_inventory' in the validated data from SorageRackSerializer
validated_data = StorageRackSerializer(data=data)
validated_data['fk_inventory'] = InventorySerializer().data

Creating a Nested Serializer with two "Sibling" Models

I'm attempting to get a JSON output similar to this:
{
name: John Doe,
best_buy_price: 420,
best_sell_price: 69,
player_profile: {
tsn_link: https://a_link.com
}
playerlistingadvanced: { # This is where I'm having the issue
sales_minute: 7,
}
}
I have three models. playerProfile being the "main" model and playerListing and playerListingAdvanced are connected to playerProfile via a one-to-one relationship. playerPfofile will have its own endpoint, but I'd also like to create an endpoint that is primarily the listings and advanced listing data (as seen above).
Here is a stripped down version of the model.py file:
class PlayerProfile(models.Model):
card_id = models.CharField(max_length=120, unique=True, primary_key=True)
name = models.CharField(max_length=420, null=True)
tsn_link = models.CharField(max_length=420, null=True)
class PlayerListing(models.Model):
player_profile = models.OneToOneField(
PlayerProfile,
on_delete=models.CASCADE,
null=True)
name = models.CharField(max_length=420, null=True)
best_sell_price = models.IntegerField(null=True)
best_buy_price = models.IntegerField(null=True)
class PlayerListingAdvanced(models.Model):
player_profile = models.OneToOneField(
PlayerProfile,
on_delete=models.CASCADE,
null=True)
sales_minute = models.DecimalField(max_digits=1000, decimal_places=2, null=True)
Here is the serializer.py I have tried, but haven't gotten to work.
class PlayerListingAdvancedForNestingSerializer(serializers.ModelSerializer):
class Meta:
model = PlayerListingAdvanced
fields = (
'sales_minute',
'last_week_average_buy',
'last_week_average_sell',
)
class PlayerListingSerializer(serializers.ModelSerializer):
player_profile = PlayerProfileForListingSerializer() # works
playerlistingadvanced = PlayerListingAdvancedForNestingSerializer() #doesn't work
class Meta:
model = PlayerListing
fields = (
'name',
'best_sell_price',
'best_buy_price',
'playerlistingadvanced',
'player_profile'
)
I'm assuming because playerListing and playerListingAdvanced are not directly related, that I'll need to do something else to make this work. Can someone point me in the right direction?
You can use SerializerMethodField. Your assuming is correct. Try this:
class PlayerListingSerializer(serializers.ModelSerializer):
player_profile = PlayerProfileForListingSerializer()
playerlistingadvanced = serializers.SerializerMethodField()
class Meta:
model = PlayerListing
fields = (
'name',
'best_sell_price',
'best_buy_price',
'playerlistingadvanced',
'player_profile'
)
def get_playerlistingadvanced(self, obj: PlayerListing):
player_profile = obj.player_profile
if hasattr(player_profile, 'playerlistingadvanced'):
serializer = PlayerListingAdvancedForNestingSerializer(instance=player_profile.playerlistingadvanced)
return serializer.data
return None

KeyError Post DjangoRestFramework

I'm new on Django Rest Framework and when I want to POST data I get a error: KeyError: 'id_area' I do not know what I'm doing wrong. Here's my code:
in my models.py
class Area(models.Model):
id_area = models.AutoField(primary_key=True)
APM = 'apm'
BUSINESS = 'business'
DESARROLLO = 'desarrollo'
SISTEMAS = 'sistemas'
ATENTUSIANOS_CHOICES = (
(APM, 'Apm'),
(BUSINESS, 'Business'),
(DESARROLLO, 'Desarrollo'),
(SISTEMAS, 'Sistemas'),
)
nombre = models.CharField(max_length=255, choices=ATENTUSIANOS_CHOICES)
class Meta:
verbose_name = 'Área'
verbose_name_plural = 'Áreas'
def __str__(self):
return self.nombre
class Atentusiano(models.Model):
id_atentusiano = models.AutoField(primary_key=True)
nombre = models.CharField(max_length=255, blank=False, null=False)
apellido = models.CharField(max_length=255, blank=False, null=False)
correo = models.CharField(max_length=255, blank=False, null=False, unique=True)
anexo = models.CharField(max_length=255, blank=True, null=True)
area = models.ForeignKey(Area, related_name='areas', on_delete=models.CASCADE)
class Meta:
verbose_name = 'Atentusiano'
verbose_name_plural = 'Atentusianos'
ordering = ['nombre']
def __str__(self):
return self.nombre + ' ' + self.apellido
in my serializers.py
class AreaSerializer(serializers.ModelSerializer):
areas = serializers.CharField(read_only=True)
class Meta:
model = Area
fields = ('id_area', 'nombre', 'areas')
class AtentusianoSerializer(serializers.ModelSerializer):
atentusianos = serializers.CharField(read_only=True)
area = serializers.CharField(source='area.nombre', read_only=True)
id_area = serializers.CharField(source='area.id_area')
class Meta:
model = Atentusiano
fields = ['id_atentusiano', 'nombre', 'apellido', 'correo', 'anexo', 'id_area', 'area', 'atentusianos']
def create(self, validated_data):
area_data = validated_data.pop('id_area')
area = models.Area.objects.create(**area_data)
atentusiano = models.Atentusiano.objects.create(area=area, **validated_data)
return atentusiano
And in my views.py
class AtentusianoView(viewsets.ModelViewSet):
queryset = Atentusiano.objects.all()
serializer_class = AtentusianoSerializer
class AreaView(viewsets.ModelViewSet):
queryset = Area.objects.all()
serializer_class = AreaSerializer
The problem is that when I want to Post data, for example:
{
"nombre": "name",
"apellido": "lastname",
"correo": "email#gmail.com",
"anexo": "1364",
"id_area": "1"
}
i got this error area_data = validated_data.pop('id_area')
KeyError: 'id_area'
I need help please
you should pop like this,
class AtentusianoSerializer(serializers.ModelSerializer):
.....
.....
class Meta:
model = Atentusiano
fields = ['id_atentusiano', 'nombre', 'apellido', 'correo', 'anexo', 'id_area', 'area', 'atentusianos']
def create(self, validated_data):
id_area = validated_data.pop('area')['id_area'] # here the correction
area = Area.objects.create(id_area=id_area) # an additional correction
atentusiano = Atentusiano.objects.create(area=area, **validated_data)
return atentusiano
EDIT: As id_area value, you are passing a string instead of an integer which will through another error. Also not,
area = models.Area.objects.create(**area_data)
it should be,
area = Area.objects.create(id_area=id_area)

DRF PUT request on unique model field

I have the following model:
class Movie(models.Model):
name = models.CharField(max_length=800, unique=True)
imdb_rating = models.IntegerField(null=True)
movie_choice = (
('Act', 'Action'),
...........
)
movie_type = models.CharField(max_length=3, choices=movie_choice)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Hiren(models.Model):
movie = models.ForeignKey(Movie)
watched_full = models.BooleanField(default=True)
rating = models.IntegerField()
source = models.CharField(max_length=500, null=True)
watched_at = models.DateField()
quality_choice = (
..................
)
video_quality = models.CharField(max_length=3, choices=quality_choice)
created_at = models.DateField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
and serializer:
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = '__all__'
class HirenSerializer(serializers.ModelSerializer):
movie = MovieSerializer()
class Meta:
model = Hiren
fields = ('movie', 'id', 'watched_full', 'rating', 'source', 'video_quality', 'watched_at')
def update(self, instance, validated_data):
instance.movie.name = validated_data.get('movie', {}).get('name')
instance.movie.imdb_rating = validated_data.get('movie', {}).get('imdb_rating')
instance.movie.movie_type = validated_data.get('movie', {}).get('movie_type')
instance.watched_full = validated_data.get('watched_full', instance.watched_full)
instance.rating = validated_data.get('rating', instance.rating)
instance.source = validated_data.get('source', instance.source)
instance.video_quality = validated_data.get('video_quality', instance.video_quality)
instance.watched_at = validated_data.get('watched_at', instance.watched_at)
instance.movie.save()
instance.save()
return instance
When I try to send a put request without changing name field from Movie model it throws an error
{
"movie": {
"name": [
"movie with this name already exists."
]
}
}
However, I can perfectly update any other field if I change the name field's value each time.
The problem is in Movie model defined by you.
When you set the name field of Movie model as unique = True,then any new entry with same movie name will always throw an error.
In your model,
class Movie(models.Model):
name = models.CharField(max_length=800, unique=True)
imdb_rating = models.IntegerField(null=True)
movie_choice = (
('Act', 'Action'),
...........
)
movie_type = models.CharField(max_length=3, choices=movie_choice)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
If you want to add two entries with the same name,remove the line unique = True or make sure to save every entry with a different name.
Or,if you want to update the record/entry then you don't need to assign a value for name field,just remove that line from your code,alternatively check if the name of the movie is already same with an improvement in the code like this :
class HirenSerializer(serializers.ModelSerializer):
movie = MovieSerializer()
class Meta:
model = Hiren
fields = ('movie', 'id', 'watched_full', 'rating', 'source', 'video_quality', 'watched_at')
def update(self, instance, validated_data):
movie_name = validated_data.get('movie', {}).get('name')
if movie_name != instance.movie.name :
instance.movie.name = movie_name
instance.movie.imdb_rating = validated_data.get('movie', {}).get('imdb_rating')
instance.movie.movie_type = validated_data.get('movie', {}).get('movie_type')
instance.watched_full = validated_data.get('watched_full', instance.watched_full)
instance.rating = validated_data.get('rating', instance.rating)
instance.source = validated_data.get('source', instance.source)
instance.video_quality = validated_data.get('video_quality', instance.video_quality)
instance.watched_at = validated_data.get('watched_at', instance.watched_at)
instance.save()
return instance
Hope this helps,Thanks.