I ask this question here because, in my searches, this error has been generally related to queries rather than ForeignKey assignment.
The error I am getting occurs in a method of a model. Here is the code:
class Deal(models.Model):...model_fields...def _update_existing_deal(self, deal_dict):#deal made from deal_dict here, psuedo code belowdeal = Deal(deal_dict)HistoricalDeal().create_historical_deal(deal)self.price = deal_dict.get('price', self.price)if self.comment != deal_dict.get['comment']:self.comment = deal_dict.get('comment', '')self.set_unit_price()logger.debug('Existing deal, (pk: %d), updated.',self.pk)class HistoricalDeal(models.Model):deal = models.ForeignKey(Deal)created_at = models.DateTimeField(auto_now_add=True)price = models.DecimalField(max_digits=8, decimal_places=2, blank=True,default=0)unit_price = models.DecimalField(decimal_places=2, max_digits=6,null=True, blank=True)def create_historical_deal(self, deal):self.deal = dealself.price = deal.priceself.unit_price = deal.unit_priceself.save()logger.debug('HistoricalDeal created for Deal with pk: %d.',deal.pk)def __str__(self):return ', '.join([self.deal.name, self.created_at.date()])
The thing is, the Deal
I am passing to HistoricalDeal.create_historical_deal
is legit. Here's a picture of the debugger in PyCharm.
For search engines, the message there is:
Unable to get repr for <class 'deals.models.HistoricalDeal'>
Any ideas?
Edit: Full code for Deal below:
class Deal(models.Model):LUMBER = 'lumber'WOODBLANK = 'woodblank'DOWEL = 'dowel'VENEER = 'veneer'PLYWOOD = 'plywood'TYPE_CHOICES = ((LUMBER, 'Lumber'),(WOODBLANK, 'Wood Blank'),(DOWEL, 'Dowel'),(VENEER, 'Veneer'),(PLYWOOD, 'Plywood'),)# define the correct method and unit for each material type# mainly used in `get_unit_price`MATERIAL_MAPPING = {LUMBER: {'method': lambda self: float(self.price) / (float(self.board_feet) or 1),'unit': 'BF',},WOODBLANK: {'method': lambda self: self.price,'unit': 'Purchase',},DOWEL: {'method': lambda self: float(self.price) / (float(self.linear_feet) or 1),'unit': 'LF',},VENEER: {'method': lambda self: float(self.price) / (float(self.square_feet) or 1),'unit': 'SF',},PLYWOOD: {'method': lambda self: float(self.price) / (float(self.square_feet) or 1),'unit': 'SF',}}name = models.CharField(max_length=200)slug = models.SlugField(max_length=100)url = models.CharField(max_length=200, blank=True)vendor = models.ForeignKey('Vendor')category = models.ForeignKey('Category')active = models.BooleanField(default=True)created_at = models.DateTimeField(auto_now_add=True)updated_at = models.DateTimeField(auto_now=True)price = models.DecimalField(max_digits=8, decimal_places=2, blank=True,default=0)comment = models.TextField(blank=True)img = models.ImageField(blank=True)unique_lot = models.IntegerField(default=None, blank=True, null=True)material_type = models.CharField(max_length=9, choices=TYPE_CHOICES)# attributes specific to material typesboard_feet = models.DecimalField(decimal_places=2, max_digits=6,null=True, blank=True)thickness = models.CharField(max_length=15,null=True, blank=True)length = models.CharField(max_length=15,null=True, blank=True)width = models.CharField(max_length=15,null=True, blank=True)diameter = models.CharField(max_length=15,null=True, blank=True)linear_feet = models.DecimalField(decimal_places=2, max_digits=6,null=True, blank=True)square_feet = models.DecimalField(decimal_places=2, max_digits=6,null=True, blank=True)adhesive_backing = models.NullBooleanField(default=False,null=True, blank=True)image = models.ForeignKey('Image', null=True, blank=True)unit_price = models.DecimalField(decimal_places=2, max_digits=6,null=True, blank=True)def set_unit_price(self):method = self.MATERIAL_MAPPING[self.material_type]['method']self.unit_price = method(self)self.save()@propertydef get_unit_price(self):method = self.MATERIAL_MAPPING[self.material_type]['method']unit = self.MATERIAL_MAPPING[self.material_type]['unit']return {'value': method(self),'units': unit}@classmethoddef _find_matching_deal(cls, deal_dict):""" Check for an existing deal that matches `deal_dict` """# TODO: use get_or_create?match = cls.objects.filter(material_type=deal_dict.get('deal_type', None),board_feet=deal_dict.get('boardfeet', None),thickness=deal_dict.get('thickness', None),length=deal_dict.get('length', None),width=deal_dict.get('width', None),diameter=deal_dict.get('diameter', None),linear_feet=deal_dict.get('linear_feet', None),square_feet=deal_dict.get('square_feet', None),adhesive_backing=deal_dict.get('adhesive_backing', None),unique_lot=deal_dict.get('unique_lot', None),category=deal_dict['category'],url=deal_dict['url'])if not match:return None# Because of the unique constraint, there should only be one matchassert len(match) == 1return match[0]@staticmethoddef _guess_category(name, url):""" Find the category that best matches the deal name/url """name = name.lower()url = url.lower()# create a string of unique possible name variantssearch_string = '|'.join({name,name.replace(' ', ''),name.replace('_', ' '),name.replace('-', ' '),name.replace('_', ''),name.replace('-', ''),url.replace(' ', ''),url.replace('-', ' '),url.replace('_', ' '),url.replace('-', ''),url.replace('_', ''),})# TODO: cache categories, don't query each timeall_categories = Category.objects.all()# get a list of categories that might matchmatching_categories = [category for category in all_categoriesif category.name.lower() in search_string]logger.debug('Found these category matches for %s: %s', name,matching_categories)if len(matching_categories) == 0:matching_categories = [category for category in all_categoriesif category.name.replace(' ', '').lower() in search_string]if len(matching_categories) == 0:# add it to the Misc categoryreturn Category.objects.get_or_create(name="Miscellaneous",defaults={'slug': 'misc'})[0]# return the first matchreturn matching_categories[0]@staticmethoddef _get_vendor(vendor_name):return Vendor.objects.get_or_create(name=vendor_name,defaults={'shipping': False})[0]@staticmethoddef _capitalize_name(name):return name.replace('-', ' ').replace('_', ' ').title()def _update_existing_deal(self, deal_dict):self.price = deal_dict.get('price', self.price)if self.comment != deal_dict.get['comment']:self.comment = deal_dict.get('comment', '')self.set_unit_price()logger.debug('Existing deal, (pk: %d), updated.',self.pk)@classmethoddef save_from_dict(cls, deal_dict):logger.debug('saving deal from dict: %s', deal_dict)deal_dict['category'] = cls._guess_category(deal_dict['name'], deal_dict['url'])deal_dict['name'] = cls._capitalize_name(deal_dict['name'])existing_deal = cls._find_matching_deal(deal_dict)if not existing_deal:logger.debug('This is a new deal, saving it')current_deal = cls.objects.create(name=deal_dict.get('name'),slug=deal_dict.get('slug', ''),url=deal_dict.get('url'),image=Image.from_url(deal_dict.get('image_url', None)),price=deal_dict.get('price'),comment=''.join(deal_dict.get('comment', [])),material_type=deal_dict.get('deal_type', None),board_feet=deal_dict.get('boardfeet', None),thickness=deal_dict.get('thickness', None),length=deal_dict.get('length', None),width=deal_dict.get('width', None),diameter=deal_dict.get('diameter', None),linear_feet=deal_dict.get('linear_feet', None),square_feet=deal_dict.get('square_feet', None),adhesive_backing=deal_dict.get('adhesive_backing', None),unique_lot=deal_dict.get('unique_lot', None),category=deal_dict['category'],vendor=cls._get_vendor(deal_dict['vendor_name']),)current_deal.set_unit_price()else:logger.debug('Existing deal, updating it (pk: %d)',existing_deal.pk)HistoricalDeal().create_historical_deal(existing_deal)existing_deal._update_existing_deal(deal_dict)def __str__(self):return '<Deal: %d, %s>' % (self.pk, self.name)class Meta(object):unique_together = (('material_type','board_feet','thickness','length','width','diameter','linear_feet','square_feet','adhesive_backing','unique_lot','category','url'),)