CommandError: One or more models did not validate - python
Under Django, for a project that worked with previous versions of Python and Django (read: maybe this is a porting question), I get:
CommandError: One or more models did not validate:
directory.phone: Reverse query name for field 'entity' clashes with field 'Entity.phone'. Add a related_name argument to the definition for 'entity'.
What do I need to do? My models.py file is below:
#!/usr/bin/python
# coding = UTF-8
from django.contrib import admin
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db import models
import datetime
import directory
import django.forms
import re
OFFICE_CHOICES = (
(u'CN', u'Chicago North Office, Illinois, USA'),
(u'CS', u'Chicago South Office, Illinois, USA'),
(u'WH', u'Wheaton Office, Illinois, USA'),
(u'SY', u'Sydney Office, New South Wales, Australia'),
)
EDIT_CHOICES = (
(u'a', u'Foreign key relationship changed.'),
(u'b', u'Image changed.'),
(u'c', u'Instance created.'),
(u'd', u'Instance deleted.'),
(u'e', u'Many to many relationship added.'),
(u'f', u'Many to many relationship deleted.'),
(u'g', u'One to many relationship added.'),
(u'h', u'One to many relationship deleted.'),
(u'i', u'Text changed.'),
)
TIME_ZONE_CHOICES = (
(None, "Select"),
("1.0", "A: Paris, +1:00"),
("2.0", "B: Athens, +2:00"),
("3.0", "C: Moscow, +3:00"),
("4.0", "D: Dubai, +4:00"),
("4.5", "-: Kabul, +4:30"),
("5.0", "E: Karachi, +5:00"),
("5.5", "-: New Delhi, +5:30"),
("5.75", "-: Kathmandu, :5:45"),
("6.0", "F: Dhaka, +6:00"),
("6.5", "-: Rangoon, +6:30"),
("7.0", "G: Jakarta, +7:00"),
("8.0", "H: Kuala Lumpur, +8:00"),
("9.0", "I: Tokyo, +9:00"),
("9.5", "-: Adelaide, +9:30"),
("10.0", "K: Sydney, +10:00"),
("10.5", "-: Lord Howe Island, +10:30"),
("11.0", "L: Solomon Islands, +11:00"),
("11.5", "-: Norfolk Island, +11:50"),
("12.0", "M: Auckland, +12:00"),
("12.75", "-: Chatham Islands, +12:45"),
("13.0", "-: Tonga, +13:00"),
("14.0", "-: Line Islands, +14:00"),
("-1.0", "N: Azores, -1:00"),
("-2.0", "O: Fernando de Norohna, -2:00"),
("-3.0", "P: Rio de Janiero, -3:00"),
("-3.5", "-: St. John's, -3:50"),
("-4.0", "Q: Santiago, -4:00"),
("-4.5", "-: Caracas, -4:30"),
("-5.0", "R: New York City, -5:00"),
("-6.0", "S: Chicago, -6:00"),
("-7.0", "T: Boulder, -7:00"),
("-8.0", "U: Los Angeles, -8:00"),
("-9.0", "V: Anchorage, -9:00"),
("-9.5", "-: Marquesas Islands, -9:30"),
("-10.0", "W: Hawaii, -10:00"),
("-11.0", "X: Samoa, -11:00"),
("-12.0", "Y: Baker Island, -12:00"),
("0.0", "Z: London, +0:00"),
)
FOREIGN_KEY_RELATIONSHIP_CHANGED = u'a'
IMAGE_CHANGED = u'b'
INSTANCE_CREATED = u'c'
INSTANCE_DELETED = u'd'
MANY_TO_MANY_RELATIONSHIP_ADDED = u'e'
MANY_TO_MANY_RELATIONSHIP_DELETED = u'f'
MANY_TO_ONE_RELATIONSHIP_ADDED = u'g'
MANY_TO_ONE_RELATIONSHIP_DELETED = u'h'
TEXT_CHANGED = u'i'
class EditTrail(models.Model):
change_set = models.IntegerField()
change_type = models.CharField(max_length = 1, choices = EDIT_CHOICES)
content_object = generic.GenericForeignKey(u'content_type', u'object_id')
content_type = models.ForeignKey(ContentType)
field_name = models.TextField(null = True, blank = True)
foreign_key_added = generic.GenericForeignKey()
foreign_key_deleted = generic.GenericForeignKey()
in_effect = models.BooleanField()
instance = generic.GenericForeignKey()
ip = models.IPAddressField()
object_id = models.PositiveIntegerField()
session = models.TextField(null = True, blank = True)
text_after = models.TextField(null = True, blank = True)
text_before = models.TextField(null = True, blank = True)
timestamp = models.DateTimeField(default = datetime.datetime.now, blank =
True)
username = models.TextField(null = True, blank = True)
def format_timestamp(self):
return directory.functions.format_timestamp(self.timestamp)
class ExtensionField(models.TextField):
def __init__(self, *arguments, **keywords):
models.TextField.__init__(self, *arguments, **keywords)
def gps_validator(value):
# Create a normalized working copy of the value.
working_copy = value
working_copy = working_copy.replace(u'\n', u',')
working_copy = working_copy.replace(u'\r', u',')
working_copy = re.sub(ur',*$', '', working_copy)
working_copy = re.sub(ur',+', u',', working_copy)
if not u',' in working_copy and not \
re.match(ur'.* .* .*', working_copy):
working_copy = working_copy.replace(u' ', u',')
working_copy = re.sub(u'[\00B0\2018\2019\201C\201D\'"]', ' ', working_copy)
working_copy = working_copy.replace(u',', u', ')
working_copy = re.sub(ur'\s+', u' ', working_copy)
working_copy = working_copy.strip()
working_copy = working_copy.upper()
# Test the normalized working copy against regular expressions for different kinds of GPS format.
if re.match(ur'[-NS]? ?\d{1,3} [0-5]\d [0-5]\d(\.\d+)[NS]?, [-EW]? ?\d{1,3} [0-5]\d [0-5]\d(\.\d+)[EW]?', working_copy):
return working_copy
elif re.match(ur'[-NS]? ?\d{1,3} [0-5]\d(\.\d+)[NS]?, [-EW]? ?\d{1,3} [0-5]\d(\.\d+)[EW]?', working_copy):
return working_copy
elif re.match(ur'[-NS]? ?\d{1,3}(\.\d+)[NS]?, [-EW]? ?\d{1,3}(\.\d+)[EW]?', working_copy):
return working_copy
else:
raise ValidationError(u'We could not recognize this as a valid GPS coordinate.')
class GPSField(models.TextField):
default_error_messages = {
u'invalid': u'We could not recognize this as a valid GPS coordinate.',
}
default_validators = [gps_validator]
class Increment(models.Model):
pass
class Location(models.Model):
identifier = models.TextField(blank = True)
description = models.TextField(blank = True)
office = models.CharField(max_length=2, choices=OFFICE_CHOICES, blank =
True)
postal_address = models.TextField(blank = True)
room = models.TextField(blank = True)
coordinates = GPSField(blank = True)
class TextURLField(models.URLField):
def __init__(self, *arguments, **keywords):
models.URLField.__init__(self, *arguments, **keywords)
def get_internal_type(self):
return u'TextField'
# This class is basically the "Person" class; however, it is called "Entity"
# to emphasize that it is intended to accommodate people, offices,
# organizational units, and possibly other areas.
class Entity(models.Model):
active = models.BooleanField(blank = True)
department = models.ForeignKey(u'self', blank = True, null =
True, related_name = u'member')
description = models.TextField(blank = True)
gps = GPSField()
image_mimetype = models.TextField(blank = True, null = True)
is_invisible = models.BooleanField(default = False)
location = models.ForeignKey(u'self', blank = True, null = True,
related_name = u'occupant')
name = models.TextField(blank = True, default =
directory.settings.PLACEHOLDER_NAME)
observes_daylight_saving_time = models.BooleanField(blank = True, default
= True)
other_contact = models.TextField(blank = True)
postal_address = models.TextField(blank = True)
publish_externally = models.BooleanField(blank = True)
reports_to = models.ForeignKey(u'self', blank = True, null = True,
related_name = u'subordinate')
start_date = models.DateField(blank = True, null = True)
time_zone = models.CharField(max_length = 5, null = True, choices =
TIME_ZONE_CHOICES)
title = models.TextField(blank = True)
class Meta:
permissions = (
("view_changelog", "View the editing changelog"),
)
class Tag(models.Model):
entity = models.ForeignKey(Entity)
is_invisible = models.BooleanField(default = False)
text = models.TextField(blank = True)
def __eq__(self, other):
try:
return self.text == other.text
except:
return False
class TextEmailField(models.EmailField):
#entity = models.ForeignKey(Entity)
def __init__(self, *arguments, **keywords):
models.EmailField.__init__(self, *arguments, **keywords)
def get_internal_type(self):
return u'TextField'
class Email(models.Model):
email = TextEmailField()
entity = models.ForeignKey(Entity)
is_invisible = models.BooleanField(default = False)
class URL(models.Model):
entity = models.ForeignKey(Entity)
url = TextURLField()
is_invisible = models.BooleanField(default = False)
class Phone(models.Model):
description = models.TextField(blank = True, null = True)
entity = models.ForeignKey(Entity, blank = True)
is_invisible = models.BooleanField(default = False)
number = models.TextField(blank = True)
def __eq__(self, other):
try:
return self.remove_formatting() == other.remove_formatting()
except:
return False
def remove_formatting(self):
return re.sub(ur'\D', u'', str(self))
class Status(models.Model):
datetime = models.DateTimeField(default = datetime.datetime.now, blank = True)
entity = models.ForeignKey(Entity, blank = True)
is_invisible = models.BooleanField(default = False)
text = models.TextField(blank = True)
username = models.TextField(blank = True)
def format_timestamp(self):
return directory.functions.format_timestamp(self.datetime)
class EntityForm(django.forms.ModelForm):
class Meta:
model = Entity
fields = (u'name', u'description', u'phone', u'department',
u'postal_address', u'reports_to', u'active', u'publish_externally')
class LocationForm(django.forms.ModelForm):
class Meta:
model = Location
In the Phone definition, for the entity attribute, add this argument:
related_name = "entity_phone"
Django builds automatically reverse relationships in models, and sometimes the names it chooses clash with other existing related names. So you can explicitly set these names so they don't clash.
In your Phone Model
class Phone(models.Model):
entity = models.ForeignKey(Entity, blank = True, related_name="someName")
Firstly try to reset your data base. And delete all your migrations and then run again . If same problem persist. Improve the question .
Related
can not insert an embedded document into a model using django
I can't create an embedded document into a model using django, i'm using djongo as my database.It keeps telling me that my value must be an instance of Model:<class 'django.db.models.base.Model'> even though I have created all the fields in the model. I really need some help.... my model: class SMSHistory(models.Model): ThoiGian = models.DateField(default=datetime.date.today,null=True,blank=True) SoDienThoai = models.CharField(max_length=100,null=True,blank=True) SeriNo = models.CharField(max_length=100,null=True,blank=True) Count = models.IntegerField(default=0,null=True,blank=True) class WebHistory(models.Model): ThoiGian = models.DateField(default=datetime.date.today,null=True,blank=True) DiaChiIP = models.CharField(max_length=100,null=True,blank=True) SoDienThoai = models.CharField(max_length=100,null=True,blank=True) SeriNo = models.CharField(max_length=100,null=True,blank=True) Count = models.IntegerField(default=0,null=True,blank=True) class AppHistory(models.Model): ThoiGian = models.DateField(default=datetime.date.today,null=True,blank=True) DiaChiIP = models.CharField(max_length=100,null=True,blank=True) SoDienThoai = models.CharField(max_length=100,null=True,blank=True) SeriNo = models.CharField(max_length=100,null=True,blank=True) Count = models.IntegerField(default=0,null=True,blank=True) class CallHistory(models.Model): ThoiGian = models.DateField(default=datetime.date.today,null=True,blank=True) SoDienThoai = models.CharField(max_length=100,null=True,blank=True) SeriNo = models.CharField(max_length=100,null=True,blank=True) Count = models.IntegerField(default=0,null=True,blank=True) class History(models.Model): MaTem = models.CharField(max_length=100,null=True,blank=True) MaSP = models.CharField(max_length=100,null=True,blank=True) SMS = models.EmbeddedModelField( model_container = SMSHistory ) App = models.EmbeddedModelField( model_container = AppHistory ) Web = models.EmbeddedModelField( model_container = WebHistory ) Call = models.EmbeddedModelField( model_container = CallHistory ) my views class check(View): def get(self,request): return render(request,'website/main.html') def post(self,request): matem=request.POST.get('txtCheck') print(matem) temp=khotemact.objects.filter(MaTem=matem) print(temp[0]) tim=History.objects.filter(MaTem=temp[0].MaTem) if len(tim)==0: print('khong co') them=History.objects.create(MaTem=temp[0].MaTem,MaSP='123', SMS={'ThoiGian':'2010-1-1','SoDienThoai':'12324','SeriNo':'12343','Count':0}, App={'ThoiGian':'2010-1-1','DiaChiIP':'1','SoDienThoai':'12324','SeriNo':'1236','Count':0}, Web={'ThoiGian':'2010-1-1','DiaChiIP':'1','SoDienThoai':'12324','SeriNo':'1236','Count':0}, Call={'ThoiGian':'2010-1-1','SoDienThoai':'1233','SeriNo':'123','Count':0} ) them.save() else: print('co') # History.objects.filter(MaTem=temp[0].MaTem).update(Web={'Count':Count+1}) return HttpResponse('oke') i received an error like this ValueError at /website/check/ Value: {'ThoiGian': '2010-1-1', 'SoDienThoai': '12324', 'SeriNo': '12343', 'Count': 0} must be instance of Model: <class 'django.db.models.base.Model'> thank you
As error says, you should use model instance, and you are using dict wrong SMS={'ThoiGian':'2010-1-1','SoDienThoai':'12324','SeriNo':'12343','Count':0} right SMS = SMSHistory.objects.create(ThoiGian='2010-1-1', SoDienThoai='12324',SeriNo='12343', Count=0)
How to fill through model instances using forms or formsets
I have the following models : class criterion(models.Model): quantity = '0' quality = '1' ascending = '0' descending = '1' three = '3' five = '5' seven = '7' ten = '10' type_choices = ( (quantity,'Ποσοτικό'), (quality,'Ποιοτικό'), ) monotonicity_choices = ( (ascending,'Αύξον'), (descending,'Φθίνον'), ) a_choices = ( (three,'Τριβάθμια'), (five,'Πενταβάθμια'), (seven,'Εφταβάθμια'), (ten,'Δεκαβάθμια'), ) criterion_research = models.ForeignKey('research', on_delete=models.CASCADE,null=True) criterion_name = models.CharField(max_length = 200,verbose_name='Όνομα Κριτηρίου') criterion_type = models.CharField(max_length = 15,choices = type_choices , default = quantity,verbose_name='Τύπος') criterion_monotonicity = models.CharField(max_length = 15,choices = monotonicity_choices , default = ascending,verbose_name='Μονοτονία') criterion_a = models.CharField(max_length = 30,choices = a_choices , default = five,verbose_name='Κλίμακα',help_text='Επιλέξτε μία από τις 4 κλίμακες που θα μετρηθεί το κριτήριο.') criterion_worst = models.FloatField(default=' ',verbose_name='Χειρότερη Τιμή',help_text='Επιλέξτε τη χειρότερη τιμή την οποία μπορεί να πάρει το κριτήριο.',blank=True) criterion_best = models.FloatField(default=' ',verbose_name='Καλύτερη Τιμή',help_text='Επιλέξτε τη καλύτερη τιμή την οποία μπορεί να πάρει το κριτήριο.',blank=True) def __str__(self): return self.criterion_name class alternative(models.Model): name = models.CharField(max_length = 200,verbose_name='Όνομα Εναλλακτικής') ranking = models.IntegerField(default='1') alternative_research = models.ForeignKey('research', on_delete=models.CASCADE) criteria = models.ManyToManyField('criterion', through='criterionvalue',blank=True) def __str__(self): return self.name class criterionvalue(models.Model): value = models.IntegerField(default='0') alt = models.ForeignKey('alternative',on_delete=models.CASCADE) crit = models.ForeignKey('criterion',on_delete=models.CASCADE) I have made a view that populates the criterion instances and the alternative instances(except the manytomany field). I want to query for all the possibles criterion and alternatives and then based on that populate the criterionvaue instances.After I complete these I want then to specify the manytomany relatioship of the criterionvalue instances with the alternatives instances .I thought of making a formset of criterionvalue model then associate them(the forms made by the formset) with the alternatives and criteria using the length of the alternatives and criteria queryset(In order to render the relatioship matrix using forms) . But I think such an idea is farfetched. Any ideas on how i could achieve such thing ?
Save function in django/python duplicating entries
I'm writing a football scoring app for a local league using the same schema as the NFL's gameday DB. I created a function that will eventually run on it's own updating each player's scores. The problem comes when the function to create a new points record is run it duplicates the entry for each player, there's no error shown or anything, everything runs as expected except for the duplicate values. here are my views.py: def updatepoints(request): actual = get_object_or_404(CurrentWeek, status='active') week = actual.week season = actual.season ptsexist = Puntos.objects.filter(week=week, season=season) if ptsexist: pts = Player.objects.raw('''SELECT DISTINCT player.player_id,(SELECT (SELECT SUM(play_player.passing_yds))+(SELECT SUM(play_player.passing_tds))+(SELECT SUM(play_player.passing_twoptm))+(SELECT SUM(play_player.passing_int))+(SELECT SUM(play_player.rushing_yds))+(SELECT SUM(play_player.rushing_tds))+(SELECT SUM(play_player.rushing_twoptm))+(SELECT SUM(play_player.fumbles_lost))+(SELECT SUM(play_player.receiving_yds))+(SELECT SUM(play_player.receiving_tds))+(SELECT SUM(play_player.receiving_twoptm))+(SELECT SUM(play_player.receiving_rec))+(SELECT SUM(play_player.kicking_fgm))+(SELECT SUM(play_player.kicking_xpmade))+(SELECT SUM(play_player.fumbles_rec_tds))+(SELECT SUM(play_player.kicking_rec_tds))) AS total,id_puntos FROM player INNER JOIN play_player ON player.player_id = play_player.player_id INNER JOIN game ON play_player.gsis_id = game.gsis_id LEFT JOIN points ON player.player_id = points.player_id AND points.temporada = game.season_year AND "DraftFantasy_puntos".semana = game.week WHERE game.week = %s AND game.season_year = %s AND game.season_type != 'Warmup' AND game.season_type != 'Postseason' GROUP BY player.player_id,points.id_points''', [week, season]) for obj in pts: obj.id = obj.player_id obj.points = obj.total obj.idpoints = obj.id_points form = UpdatePointsForm(request.POST) pointsf = form.save(commit=False) pointsf.id_points = obj.idpoints pointsf.player_id = obj.player_id pointsf.temporada = season pointsf.semana = week pointsf.puntos_ppr = obj.total pointsf.save() return HttpResponseRedirect("/dashboard/") else: return HttpResponseRedirect("/savepoints/") def savepoints(request): actual = get_object_or_404(CurrentWeek, status='active') week = actual.week season = actual.season ptsn = Player.objects.raw('''SELECT DISTINCT player.player_id,(SELECT (SELECT SUM(play_player.passing_yds))+(SELECT SUM(play_player.passing_tds))+(SELECT SUM(play_player.passing_twoptm))+(SELECT SUM(play_player.passing_int))+(SELECT SUM(play_player.rushing_yds))+(SELECT SUM(play_player.rushing_tds))+(SELECT SUM(play_player.rushing_twoptm))+(SELECT SUM(play_player.fumbles_lost))+(SELECT SUM(play_player.receiving_yds))+(SELECT SUM(play_player.receiving_tds))+(SELECT SUM(play_player.receiving_twoptm))+(SELECT SUM(play_player.receiving_rec))+(SELECT SUM(play_player.kicking_fgm))+(SELECT SUM(play_player.kicking_xpmade))+(SELECT SUM(play_player.fumbles_rec_tds))+(SELECT SUM(play_player.kicking_rec_tds))) AS total FROM player INNER JOIN play_player ON player.player_id = play_player.player_id INNER JOIN game ON play_player.gsis_id = game.gsis_id WHERE game.week = %s AND game.season_year = %s AND game.season_type != 'Warmup' AND game.season_type != 'Postseason' GROUP BY player.player_id''', [week, season]) for obj in ptsn: obj.id = obj.player_id obj.points = obj.total formn = PointsForm(request.POST) pointsfn = formn.save(commit=False) pointsfn.player_id = obj.player_id pointsfn.temporada = season pointsfn.semana = week pointsfn.points = obj.total pointsfn.save() return HttpResponseRedirect("/ligas/") the forms.py: class PointsForm(forms.ModelForm): class Meta: model = Points exclude = ["player_id", "season", "week", "puntos"] class UpdatePointsForm(forms.ModelForm): class Meta: model = Points exclude = ["id_points", "player_id", "season", "week", "points"] and the models.py: class Points(models.Model): id_points = models.AutoField(primary_key=True, null=False, max_length=15) player_id = models.CharField(max_length=100) season = models.IntegerField(max_length=10) week = models.IntegerField(max_length=10) puntos = models.IntegerField(max_length=50) class CurrentWeek(models.Model): id_week = models.AutoField(primary_key=True, null=False, max_length=15) week = models.IntegerField(max_length=10) season = models.IntegerField(max_length=5) status = models.CharField(max_length=50, default="done") I'm really stumped so any help will be much appreciated.
Django advanced search
I am currently learning Django by making a web app that sell used bikes and I'm having problems with on site search. I would like to have a search field for every model field and I just can't figure out how to do it. What would be the best way to do this? Any help is more than welcome! Here is my model: class UsedBike(models.Model): manufacturers = ( ('Aprilia', 'Aprilia'), ('Benelli', 'Benelli'), ('BMW', 'BMW'), ('Cagiva', 'Cagiva'), ('Gilera', 'Gilera'), ('Harley-Davidson', 'Harley-Davidson'), ('Husaberg', 'Husaberg'), ('Husquarna', 'Husquarna'), ('Hyosung', 'Hyosung'), ('Kawasaki', 'Kawasaki'), ('KTM', 'KTM'), ('Kymco', 'Kymco'), ('Moto Guzzi', 'Moto Guzzi'), ('MV Agusta', 'MV Agusta'), ('Suzuki', 'Suzuki'), ('Tomos', 'Tomos'), ('Triumph', 'Triumph'), ('Yamaha', 'Yamaha'), ) manufacturer = models.CharField(help_text = 'Manufacturer: ', max_length = 20, choices = manufacturers) model = models.CharField(max_length = 20, help_text = 'Model: ')
I solved this but I forgot to post the answer so that anyone who have the similar problem can use it. It is not perfect but it worked for me, if someone have a better solution feel free to answer. In my models I have: class UsedBike(models.Model): manufacturer = models.CharField(max_length = 20) model = models.CharField(max_length = 20) year = models.PositiveIntegerField(default = 0) bike_type = models.CharField(max_length = 20) price = models.PositiveIntegerField(default = 0) engine_size = models.PositiveIntegerField(default = 0) And in views: def searchbike(request): man = request.GET.get('manufacturer') mod = request.GET.get('model') year_f = request.GET.get('year_from') year_t = request.GET.get('year_to') price_f = request.GET.get('price_from') price_t = request.GET.get('price_to') bike_t = request.GET.get('bike_type') capacity_f = request.GET.get('cubic_capacity_from') capacity_t = request.GET.get('cubic_capacity_to') question_set = UsedBike.objects.filter() if request.GET.get('manufacturer'): question_set = question_set.filter(manufacturer__exact = man) if request.GET.get('model'): question_set = question_set.filter(model__icontains = mod) if request.GET.get('year_from'): question_set = question_set.filter(year__gte = year_f) if request.GET.get('year_to'): question_set = question_set.filter(year__lte = year_t) if request.GET.get('price_from'): question_set = question_set.filter(price__gte = price_f) if request.GET.get('price_to'): question_set = question_set.filter(price__lte = price_t) if request.GET.get('bike_type'): question_set = question_set.filter(bike_type__exact = bike_t) if request.GET.get('cubic_capacity_from'): question_set = question_set.filter(engine_size__gte = capacity_f) if request.GET.get('cubic_capacity_to'): question_set = question_set.filter(engine_size__lte = capacity_t) return render(request, 'shop/search.html', { 'searched': question_set})
You can use django-smart-selects: If you have the following model: class Location(models.Model) continent = models.ForeignKey(Continent) country = models.ForeignKey(Country) area = models.ForeignKey(Area) city = models.CharField(max_length=50) street = models.CharField(max_length=100) And you want that if you select a continent only the countries are available that are located on this continent and the same for areas you can do the following: from smart_selects.db_fields import ChainedForeignKey class Location(models.Model) continent = models.ForeignKey(Continent) country = ChainedForeignKey( Country, chained_field="continent", chained_model_field="continent", show_all=False, auto_choose=True ) area = ChainedForeignKey(Area, chained_field="country", chained_model_field="country") city = models.CharField(max_length=50) street = models.CharField(max_length=100)
Redundant code in Django models.py. How do I improve it?
I am trying to create a task list with each task having a datetime attribute. The tasks needs to be in order with t_created being the first and t_paid being last. The order is shown in step_datetime. The description for each tasks is in STEPS. I currently have two methods all_steps and next_step that shows the task list information. The two methods also need to display the name of the user_created, but that variable won't be defined until the methods are called. That's why I am doing a string replace method. I feel like I am repeating my code a lot, and I want to follow the DRY principle of Django. Is there any way I could improve this code? Here is my full code: class Order( models.Model ) : def __unicode__( self ) : return unicode( self.id ) def comments_count( self ) : return OrderComment.objects.filter( order = self.id ).count() def all_steps( self ) : user = self.user_created.first_name steps = [] step_datetime = [ self.t_created, self.t_action, self.t_followup_one, self.t_vendor_appt_one, self.t_vendor_appt_two, self.t_work_done, self.t_followup_two, self.t_paid, ] for ( i, step ) in enumerate( self.STEPS ) : steps.append( ( step_datetime[ i ], step.replace( '<user_created>', user ), ) ) return steps def next_step( self ) : user = self.user_created.first_name step = 0 if self.t_action is None : step = 0 elif self.t_followup_one is None : step = 1 elif self.t_vendor_appt_one is None : step = 2 elif self.t_vendor_appt_two is None : step = 3 elif self.t_work_done is None : step = 4 elif self.t_followup_two is None : step = 5 elif self.paid is None : step = 6 return str( step ) + ": " + self.STEPS[ step ].replace( '<user_created>', user ) STEPS = [ "Review, then either approve or reject the order.", "Follow up with <user_created>", "Contact the vendor to get a quote and arrange an appointment for <user_created>.", "Review the quote, (get owner approval), then arrange a second appointment for the repairs.", "Confirm the finished repairs and pay the vendor.", "Follow up again with <user_created>", "Confirm payment and close the order.", ] ACTION_CHOICES = ( ( 'p', 'pending' ), ( 'a', 'approved' ), ( 'r', 'rejected' ), ( 'c', 'closed' ), ) user_created = models.ForeignKey( User, related_name = 'user_created', verbose_name = 'created by' ) user_action = models.ForeignKey( User, related_name = 'user_status' , verbose_name = 'action by' , null = True, blank = True ) t_created = models.DateTimeField( auto_now_add = True, verbose_name = 'created' ) t_action = models.DateTimeField( null = True, blank = True, verbose_name = 'action' ) t_followup_one = models.DateTimeField( null = True, blank = True, verbose_name = 'first follow-up' ) t_vendor_appt_one = models.DateTimeField( null = True, blank = True, verbose_name = 'first appointment' ) t_vendor_appt_two = models.DateTimeField( null = True, blank = True, verbose_name = 'second appointment' ) t_work_done = models.DateTimeField( null = True, blank = True, verbose_name = 'work done' ) t_followup_two = models.DateTimeField( null = True, blank = True, verbose_name = 'second follow-up' ) t_paid = models.DateTimeField( null = True, blank = True, verbose_name = 'paid' ) action = models.CharField( max_length = 1, choices = ACTION_CHOICES, default = 'p' ) quote = models.DecimalField( max_digits = 8, decimal_places = 2, null = True, blank = True ) payment = models.DecimalField( max_digits = 8, decimal_places = 2, null = True, blank = True ) items = models.ManyToManyField( Item, null = True, blank = True ) t_modified = models.DateTimeField( auto_now = True, verbose_name = 'modified' ) After accepting #Dougal's answer. I changed some of the variables around and came up with this: def all_steps( self ) : user = self.user_created.first_name return [ ( getattr( self, attr ), task.format( user = user ) ) for ( attr, task ) in self.TASKS ] def next_step( self ) : user = self.user_created.first_name task_num = next( ( i for ( i, ( attr, task ) ) in enumerate( self.TASKS ) if getattr( self, attr ) is None ), None ) if task_num == None : return "Done!" else: return "{number}: {task}".format( number = str( task_num + 1 ), task = self.TASKS[ task_num ][ 1 ].format( user = user ) ) TASKS = ( ( "t_action" , "Review, then either approve or reject the order." ), ( "t_followup_one" , "Follow up with {user}." ), ( "t_vendor_appt_one", "Contact the vendor to get a quote and arrange an appointment for {user}." ), ( "t_vendor_appt_two", "Review the quote, (get owner approval), then arrange a second appointment for the repairs." ), ( "t_work_done" , "Confirm the finished repairs and pay the vendor." ), ( "t_followup_two" , "Follow up again with {user}." ), ( "t_paid" , "Confirm payment and close the order." ), )
You can do things like: for prop in ("t_created", "t_created2" ... ): val = getattr(self, prop) # some logic that works with that, maybe uses setattr
Adding on to #Marcin's answer: You could make a tuple of the property names (say _STEP_NAMES at the module level; you could also make it at the class level, like STEPS, or even just combine the two into a tuple of pairs of attributes and names; that might be a little cleaner). Also, STEPS should probably be a tuple, since it shouldn't be modifiable at runtime. Then you can reduce your code to: def all_steps(self): user = self.user_created.first_name return [(getattr(self, attr), step.replace('<user_created>', user)) for attr, step in zip(_STEP_NAMES, self.STEPS)] def next_step(self): user = self.user_created.first_name step = next((i for i, attr in enumerate(_STEP_NAMES) if getattr(self, attr) is None), None) # assumes Python 2.6+ if step == None: return "Done!" else: return str(step) + ": " + self.STEPS[step].replace('<user_created>', user) If you need Python 2.4/2.5 compatability, the next line can be replaced by try: step = (i for i, attr in enumerate(_STEP_NAMES) if getattr(self, attr) is None).next() except StopIteration: return "Done!" return str(step) + ": " + self.STEPS[step].replace('<user_created>', user)