Django import-export - Import model with child from excel - django

I have two models: Project and Activity.
When registering a project, one or more associated activities can be added.
How can I import projects from xlsx, which include at least one activity?. I'm using the third party library django-import-export
I configure the project resource to export all the activities of each project in one cell separated by /, but I need the opposite, import all the activities of each project. I think that I must first save the Project for obtain the id, next extract the info from cell and finally save each activity associated, but I don't know how to do that.
My simplified models are:
class Project(models.Model):
reg_date= models.DateField(
default=date.today)
name = models.CharField(
max_length=100,
unique=True)
def __str__(self):
return self.name
class Activity(models.Model):
schedule= models.ForeignKey(
Project,
on_delete=models.CASCADE)
date = models.DateField()
description = models.TextField(max_length=1000)
class Meta:
unique_together = ('schedule', 'date', 'description')
class ProjectResource(resources.ModelResource):
activities = Field()
class Meta:
model = Project
import_id_fields = ['name']
exclude = ('id',)
skip_unchanged = True
report_skipped = True
fields = ('reg_date',
'name',
'activities')
def dehydrate_activities(self, obj):
if obj.id:
return "/".join([
'({0} - {1})'.format(activity.date, activity.description) for activity in obj.projectactivity_set.all()
])
def skip_row(self, instance, original, row, import_validation_errors=None):
if original.name:
return True
return False
An example of exported file is:
reg_date
name
activities
2023-01-10
Project 1
2023-01-12-This is the first activity/2023-01-14-This is the second activity
2023-01-10
Project 2
2023-01-13-This is the first activity/2023-01-15-This is the second activity

You need to create the Activity instances before you create the Project instances.
Then in your Project resource class you can define that a particular field is for foreign keys.
I've got an example;
from import_export import fields, resources, widgets
from apps.event.models import Occurrence
from ..models import Token
class TokenResource(resources.ModelResource):
""" Integrate django-import-export with the Token model """
occurrence = fields.Field(
column_name='occurrence',
attribute='occurrence',
widget=widgets.ForeignKeyWidget(Occurrence, 'id')
)
class Meta:
fields = (
'id',
'occurrence',
'code',
)
model = Token
When using the ForeignKeyWidget, the first argument is the related model, then the second is a unique value that you can use to lookup the instance. It's that unique value that you then put in your import file to reference the related objects.
And my Token model has that relationship;
class Token(EnabledMixin, TimestampMixin, models.Model):
""" Token for event entry. """
class Meta:
""" Meta class definition. """
app_label = 'console'
verbose_name = _("Token")
verbose_name_plural = _("Tokens")
unique_together = ('code', 'occurrence')
ordering = [
'id',
]
occurrence = models.ForeignKey(
to='event.Occurrence',
verbose_name=_("Event Occurrence"),
blank=True,
null=True,
related_name='tokens',
on_delete=models.SET_NULL,
)

Related

How to print an object of a foreignkey instead of the entire class to Django administration

In my 'models.py' file, I have 2 LOOKUP tables: the 'Part' class and the 'Vendor' class as shown:
# Part Lookup table
class Part(models.Model):
part_id = models.CharField(max_length=64) # Part ID
description = models.CharField(max_length=64) # Part description
pq = models.DecimalField(max_digits=7, decimal_places=2) # Pack quantity
mrrp = models.DecimalField(max_digits=10, decimal_places=2) # Manufacturers Recommended Retail Price
# Display something in admin
def __str__(self):
return f"{self.id} {self.part_id} {self.description} {self.pq} {self.mrrp}"
# Vendor Lookup table
class Vendor(models.Model):
name = models.CharField(max_length=64)
# Display something in admin
def __str__(self):
return f"{self.id} {self.name}"
I then have another FACT or ASSOCIATION table, the 'Relationship' class as shown:
# Relationship association table (Between Part & Vendor)
class Relationship(models.Model):
part_id = models.ForeignKey(Part, on_delete=models.CASCADE, related_name="part")
vendor_id = models.ForeignKey(Vendor, on_delete=models.CASCADE, related_name="vendor")
# Display something in admin
def __str__(self):
return f"{self.id} {self.part_id} {self.vendor_id}"
The below is my entire 'admin.py' file:
from django.contrib import admin
# Import the classes from models.py
from .models import Part, Vendor, Relationship, Transaction, Offer, Supersession, Category, Group
# Django admin title
admin.site.site_header = "Online Parts System"
# Model Admin class
class PartAdmin(admin.ModelAdmin):
# Data headers (must match objects of the Part class)
list_display = ('id', 'part_id', 'description', 'pq', 'mrrp')
# Model Admin class
class VendorAdmin(admin.ModelAdmin):
# Data headers (must match objects of the Vendor class)
list_display = ('id', 'name')
# Model Admin class
class RelationshipAdmin(admin.ModelAdmin):
# Data headers (must match objects of the Vendor class)
list_display = ('id', 'part_id', 'vendor_id')
# Register your models here.
admin.site.register(Part, PartAdmin)
admin.site.register(Vendor, VendorAdmin)
admin.site.register(Relationship, RelationshipAdmin)
Everything is working really well so far. Unfortunately, in the Django admin page > Relationships table, under the 'Part ID' column, I am seeing every object of the 'Part' class (since I used the ForeignKey line).
I only wish to see one object such as 'part_id' instead of all the others combined.
What is the simplest way to achieve this?
Please see image below. I basically want to simplify the string underlined in red.
Django Admin Page So Far
The string used in the admin dashboard under the part_id column comes from the __str__ method of the Part model. You can change the implementation of the __str__ method to include only the part_id.
# Part Lookup table
class Part(models.Model):
part_id = models.CharField(max_length=64) # Part ID
description = models.CharField(max_length=64) # Part description
pq = models.DecimalField(max_digits=7, decimal_places=2) # Pack quantity
mrrp = models.DecimalField(max_digits=10, decimal_places=2) # Manufacturers Recommended Retail Price
# Display something in admin
def __str__(self):
return f"{self.part_id}"

Need to get the Foreign key value instead of ID in the database table using Django

model
class Project(models.Model):
project_name = models.CharField(max_length=20)
client= models.ForeignKey(Client,on_delete=CASCADE,related_name="Client1",default=None)
user=models.ManyToManyField(Default_User,related_name='users',default=None)
description=models.TextField()
type=models.TextField() #dropdown
start_date = models.DateTimeField(max_length=10)
end_date=models.DateTimeField(max_length=10)
technical_contact_name = models.CharField(max_length=30)
email=models.EmailField(max_length=254,default=None)
phone = PhoneField(blank=True)
delivery_head_contact_name=models.CharField(max_length=30)
class Meta:
db_table ='Project'
def __str__(self):
return self.project_name
model
class Job(models.Model):
job_name=models.CharField(max_length=50)
user= models.ForeignKey(Default_User,on_delete=CASCADE)
project = ChainedForeignKey(Project,chained_field="user", chained_model_field="user",related_name='projects',show_all=False, auto_choose=True, sort=True)
date = models.DateField(max_length=10,default=None)
class Meta:
db_table ='Job'
def __str__(self):
return '{}'.format(self.job_name)
serializers
class ProjectSerializers(serializers.ModelSerializer):
class Meta:
model= Project
fields= '__all__'
class Job_Serializers(serializers.ModelSerializer):
job = serializers.StringRelatedField()
class Meta:
model= Job
fields= ('id','job_name','user','project','date','job',)
I need to get the foreign key value displayed in the database table of Job model but as per the above code it is displaying the Foreign key ID. For example I linked the project model in the Job model and in the db table it is showing the Project_id as(1,2,3) but i need to return the values of that id as(app, learning etc). Please help me to get the values of the foreign key value instead of ID in the database table.
The database will by default take the unique field from the model and django provide id as unique key for models. It is for data consistency. So you can let that happen and in job serializera use SerializerMethodField to retrieve the value of project name based on instance of job objects.
Depends on what you want to achieve with it. If it is just to return another field value from project, then you can add it to the serializer as in below example. I am returning project_name as well.
class JobSerializers(serializers.ModelSerializer):
job = serializers.StringRelatedField()
project_name = serializers.SerializerMethodField()
class Meta:
model= Job
fields= ('id','job_name','user','project','date','job', 'project_name')
def get_project_name(self, job):
return job.project.project_name
If you want to return the whole project object then you have to include
project = ProjectSerializers()

ModelMultipleChoiceFilter - Field 'id' expected a number but got 'Diner'

So I have a simple Ad model and a FilterView showing all the ads. The ads can be filtered by different tags stored in a separate model joined by a ManyToManyField.
I'm using django-filter to set up a small ModelMultipleChoiceFilter and let users select different tags to filter the Ads. This is working however it uses the tag__id. I would like it to use the tag__slug field.
Therefore I've added the attribute "to_field_name='slug'" but I get the following;
Field 'id' expected a number but got 'diner'.
The following code does work but only filters by tag__id like:
/?tags=6
and I would rather see something like this;
?tags=diner
models.py
class Ad(models.Model):
category = models.ForeignKey('Category', on_delete=models.SET_NULL, null=True)
description = RichTextField()
tags = models.ManyToManyField('Tag')
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, null=True)
class Meta:
ordering = ['-title']
def __str__(self):
return self.title
class Tag(models.Model):
name = models.CharField(max_length=200, help_text='Titel van de tag')
slug = models.SlugField(max_length=200, null=True)
def __str__(self):
return self.name
filters.py
from django import forms
from discovery.grid.models import Ad, Tag
import django_filters
class AdFilter(django_filters.FilterSet):
tags = django_filters.ModelMultipleChoiceFilter(
# to_field_name='slug',
queryset=Tag.objects.all(),
widget=forms.CheckboxSelectMultiple)
class Meta:
model = Ad
fields = [
'tags'
]
How can I achieve filtering based on the model name or slug instead of the id?
With best regards,
Maybe you can try like this:
class AdFilter(django_filters.FilterSet):
tags = CharFilter(method='my_custom_filter')
def my_custom_filter(self, queryset, name, value):
return queryset.filter(**{
'tags__slug__iexact': value,
})
class Meta:
model = Ad
fields = [
'tags'
]
More information can be found in documentation.

django rest framework - Nested serialization not including nested object fields

i'm trying to get nested object fields populated, however the only thing being returned is the primary key of each object (output below):
{
"name": "3037",
"description": "this is our first test product",
"components": [
1,
2,
3,
4
]
}
How do I have the component model's fields populated as well (and not just the PKs)? I would like to have the name and description included.
models.py
class Product(models.Model):
name = models.CharField('Bag name', max_length=64)
description = models.TextField ('Description of bag', max_length=512, blank=True)
urlKey = models.SlugField('URL Key', unique=True, max_length=64)
def __str__(self):
return self.name
class Component(models.Model):
name = models.CharField('Component name', max_length=64)
description = models.TextField('Component of product', max_length=512, blank=True)
fits = models.ForeignKey('Product', related_name='components')
def __str__(self):
return self.fits.name + "-" + self.name
serializers.py
from rest_framework import serializers
from app.models import Product, Component, Finish, Variant
class componentSerializer(serializers.ModelSerializer):
class Meta:
model = Component
fields = ('name', 'description', 'fits')
class productSerializer(serializers.ModelSerializer):
#components_that_fit = componentSerializer(many=True)
class Meta:
model = Product
fields = ('name', 'description', 'components')
#fields = ('name', 'description', 'components_that_fit' )
The documented approach doesn't seem to be working for me, and gives me the following error (you can see the lines following the standard approach commented out in the serializers.py entry above:
Got AttributeError when attempting to get a value for field 'components_that_fit' on serializer 'productSerializer'.
The serializer field might be named incorrectly and not match any attribute or key on the 'Product' instance.
Original exception text was: 'Product' object has no attribute 'components_that_fit'.
Update based on answer
Thanks to #Carlton's answer below, here's what is working for me:
serializers.py was changed and now looks like this:
class productSerializer(serializers.ModelSerializer):
components = componentSerializer(many=True)
class Meta:
model = Product
fields = ('name', 'description', 'components')
By calling the field components_that_fit, you're having the serialiser look for an attribute by that name. (There isn't one, hence your error.)
Two ways to fix it:
Call the field components, but declare it as components = componentSerializer(many=True)
Set source='components' field option when declaring the components_that_fit field.
The reason you get primary keys is that, unless declared explicitly, relations default to PrimaryKeyRelatedField.
I hope that helps.

Django-tables2: Change text displayed in column title

I am working with a MySQL view (Create View as Select ...) and have successfully manged to connect the view to a model like this:
#models.py
class Dashboard(models.Model):
devenv = models.CharField(max_length=30, primary_key=True)
numberofissues = models.BigIntegerField()
class Meta:
managed=False
db_table = 'stability_dashboard'
I have also managed to display data in a table using the boiler plate code from the example:
#tables.py
class DashboardTable(tables.Table):
class Meta:
model = Dashboard
attrs = {'class': 'paleblue'}
#views.py
def dashboard(request):
table = DashboardTable(Dashboard.objects.all())
RequestConfig(request).configure(table)
return render(request, 'uptime/dash.html', {'table': table})
I would now like to change the title displayed in each column to something more understandable including spaces e.g. Instead of 'devenv' => 'Development Environment'
Just add the columns whose names you want to override in your tables.py. For instance
#tables.py
import django_tables2 as tables
from models import Dashboard
class DashboardTable(tables.Table):
devenv = tables.Column(verbose_name= 'Development Environment' )
class Meta:
model = Dashboard
attrs = {'class': 'paleblue'}
Another (probably more DRY) solution is to leave tables.py as is and add verbose_name in your model definition:
#models.py
class Dashboard(models.Model):
devenv = models.CharField(max_length=30, primary_key=True, verbose_name='Development Environment')
numberofissues = models.BigIntegerField(verbose_name='Number of Issues')
class Meta:
managed=False
db_table = 'stability_dashboard'