I would like to import a CSV in Django. The issue occurs when trying to import based on the attributes. Here is my code:
class Event(models.Model):
id = models.BigAutoField(primary_key=True)
amount = models.ForeignKey(Amount, on_delete=models.CASCADE)
value = models.FloatField()
space = models.ForeignKey(Space, on_delete=models.RESTRICT)
time = models.ForeignKey(Time, on_delete=models.RESTRICT)
class Meta:
managed = True
db_table = "event"
class Space(models.Model):
objects = SpaceManager()
id = models.BigAutoField(primary_key=True)
code = models.CharField(max_length=100)
type = models.ForeignKey(SpaceType, on_delete=models.RESTRICT)
space_date = models.DateField(blank=True, null=True)
def natural_key(self):
return self.code # + self.type + self.source_date
def __str__(self):
return f"{self.name}"
class Meta:
managed = True
db_table = "space"
class Time(models.Model):
objects = TimeManager()
id = models.BigAutoField(primary_key=True)
type = models.ForeignKey(TimeType, on_delete=models.RESTRICT)
startdate = models.DateTimeField()
enddate = models.DateTimeField()
def natural_key(self):
return self.name
def __str__(self):
return f"{self.name}"
class Meta:
managed = True
db_table = "time"
Now, I create the resource that should find the right objects, but it seems it does not enter into ForeignKeyWidget(s)
at all:
class AmountForeignKeyWidget(ForeignKeyWidget):
def clean(self, value, row=None, **kwargs):
logger.critical("<<<<< {AmountForeignKeyWidget} <<<<<<<")
name_upper = value.upper()
amount = Amount.objects.get_by_natural_key(name=name_upper)
return amount
class SpaceForeignKeyWidget(ForeignKeyWidget):
def clean(self, value, row, **kwargs):
logger.critical("<<<<< {SpaceForeignKeyWidget} <<<<<<<")
space_code = row["space_code"]
space_type = SpatialDimensionType.objects.get_by_natural_key(row["space_type"])
try:
space_date = datetime.strptime(row["space_date"], "%Y%m%d")
except ValueError:
space_date = None
space = Space.objects.get(
code=space_code, type=space_type, source_date=space_date
)
return space
class TimeForeignKeyWidget(ForeignKeyWidget):
def clean(self, value, row, **kwargs):
logger.critical("<<<<< {TimeForeignKeyWidget} <<<<<<<")
time_type = TimeType.objects.get_by_natural_key(row["time_type"])
time_date = parse_datetime(row["time_date"])
time = Time.objects.get_or_create(
type=time_type, startdate=time_date), defaults={...}
)
return time
class EventResource(ModelResource):
amount = Field(
column_name="amount",
attribute="amount",
widget=AmountForeignKeyWidget(Amount),
)
space = Field(
# column_name="space_code",
attribute="space",
widget=SpaceForeignKeyWidget(Space),
)
time = Field(
attribute="time",
widget=TimeForeignKeyWidget(Time),
)
def before_import_row(self, row, row_number=None, **kwargs):
logger.error(f">>>> before_import_row() >>>>>>")
time_date = datetime.strptime(row["time_date"], "%Y%m%d").date()
time_type = TimeType.objects.get_by_natural_key(row["time_type"])
Time.objects.get_or_create(
type=time_type, startdate=time_date,
defaults={
"name": str(time_type) + str(time_date),
"type": time_type,
"startdate": time_date,
"enddate": time_date + timedelta(days=1),
},
)
class Meta:
model = Event
I added some loggers, but I only print out the log at AmountForeignKeyWidget
. The main question is: How to search for objects in Space
by attributes (space_code
,space_type
,space_date
) and in Time
search and create by (time_date
,time_type
)
A lesser question is why SpaceForeignKeyWidget
and TimeForeignKeyWidget
are not used?
I managed to solve all the issues and make proper imports. Following is the code I used:
class EventResource(ModelResource):
amount = Field(
column_name="amount",
attribute="amount",
widget=ForeignKeyWidget(Amount, field="name__iexact"),
)
space_code = Field(
attribute="space",
widget=SpaceForeignKeyWidget(Space),
)
time_date = Field(
attribute="time",
widget=TimeForeignKeyWidget(Time),
)
class Meta:
model = Event
For the amount
field I don't need to make a derivative Widget, since it is using only one variable in CSV. For the two others, implementation follows. I noticed that the widgets for the two other variables were not called and the reason is the variable names were non-existent in my CSV file. When I renamed them to the column names existing in the CSV they have been called.
class SpaceForeignKeyWidget(ForeignKeyWidget):
def clean(self, value, row, **kwargs):
space_code = row["spacial_code"]
space_type = SpaceDimensionType.objects.get(type=row["space_type"])
try:
space_date = datetime.strptime(row["space_date"], "%Y%m%d")
except ValueError:
space_date = None
space = SpaceDimension.objects.get(
code=space_code, type=space_type, source_date=space_date
)
return space
class TimeForeignKeyWidget(ForeignKeyWidget):
def clean(self, value, row, **kwargs):
time_type = TimeDimensionType.objects.get(type=row["time_type"])
delta = T_TYPES[time_type]
start_date = datetime.strptime(row["time_date"], "%Y%m%d").date()
end_date = start_date + timedelta(days=delta)
time, created = TimeDimension.objects.get_or_create(
type=time_type,
startdate=start_date,
enddate=start_date + timedelta(days=delta),
defaults={
"name": f"{time_type}: {start_date}-{end_date}",
"type": time_type,
"startdate": start_date,
"enddate": end_date,
},
)
return temporal
SpaceForeignKeyWidget
only searches it the record is existing and returns the object and TimeForeignKeyWidget
creates if non-existing and returns the record. This way no need to use before_import_row()
and all the logic is localized to this two widgets.