1 from django.db import models
  2 
  3 # Create your models here.
  4 
  5 from django.contrib.contenttypes.models import ContentType
  6 from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
  7 
  8 ######################################## 课程表 ########################################
  9 
 10 class Course(models.Model):
 11     """
 12     专题课程
 13     """
 14     name = models.CharField(max_length=128, unique=True, verbose_name="模块")
 15     course_img = models.CharField(max_length=255)
 16     course_type_choices = ((0, '付费'), (1, 'VIP专享'), (2, '学位课程'))
 17     course_type = models.SmallIntegerField(choices=course_type_choices)
 18     brief = models.TextField(verbose_name="课程概述", max_length=2048)
 19     level_choices = ((0, '初级'), (1, '中级'), (2, '高级'))
 20     level = models.SmallIntegerField(choices=level_choices, default=1)
 21     pub_date = models.DateField(verbose_name="发布日期", blank=True, null=True)
 22     period = models.PositiveIntegerField(verbose_name="建议学习周期(days)", default=7)
 23     order = models.IntegerField("课程顺序", help_text="从上一个课程数字往后排")
 24     attachment_path = models.CharField(max_length=128, verbose_name="课件路径", blank=True, null=True)
 25     status_choices = ((0, '上线'), (1, '下线'), (2, '预上线'))
 26     status = models.SmallIntegerField(choices=status_choices, default=0)
 27 
 28     order_details = GenericRelation("OrderDetail", related_query_name="course")
 29     coupon = GenericRelation("Coupon")
 30     price_policy = GenericRelation("PricePolicy")  # 用于GenericForeignKey反向查询,不会生成表字段,切勿删除,如有疑问请联系老村长
 31 
 32     def __str__(self):
 33         return "%s(%s)" % (self.name, self.get_course_type_display())
 34 
 35 class CourseDetail(models.Model):
 36     """课程详情页内容"""
 37 
 38     course = models.OneToOneField("Course",on_delete=models.CASCADE)
 39     hours = models.IntegerField("课时")
 40     course_slogan = models.CharField(max_length=125, blank=True, null=True)
 41     video_brief_link = models.CharField(max_length=255, blank=True, null=True)
 42     why_study = models.TextField(verbose_name="为什么学习这门课程")
 43     what_to_study_brief = models.TextField(verbose_name="我将学到哪些内容")
 44     career_improvement = models.TextField(verbose_name="此项目如何有助于我的职业生涯")
 45     prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024)
 46     recommend_courses = models.ManyToManyField("Course", related_name="recommend_by", blank=True)
 47     teachers = models.ManyToManyField("Teacher", verbose_name="课程讲师")
 48 
 49     def __str__(self):
 50         return "%s" % self.course
 51 
 52 class Teacher(models.Model):
 53     """讲师、导师表"""
 54 
 55     name = models.CharField(max_length=32)
 56     role_choices = ((0, '讲师'), (1, '导师'))
 57     role = models.SmallIntegerField(choices=role_choices, default=0)
 58     title = models.CharField(max_length=64, verbose_name="职位、职称")
 59     signature = models.CharField(max_length=255, help_text="导师签名", blank=True, null=True)
 60     image = models.CharField(max_length=128)
 61     brief = models.TextField(max_length=1024)
 62 
 63     def __str__(self):
 64         return self.name
 65 
 66 class PricePolicy(models.Model):
 67     """价格与有课程效期表"""
 68     content_type = models.ForeignKey(ContentType,on_delete=models.CASCADE)  # 关联course or degree_course
 69     object_id = models.PositiveIntegerField()
 70     content_object = GenericForeignKey('content_type', 'object_id')
 71     # course = models.ForeignKey("Course")
 72     valid_period_choices = ((1, '1天'), (3, '3天'),
 73                             (7, '1周'), (14, '2周'),
 74                             (30, '1个月'),
 75                             (60, '2个月'),
 76                             (90, '3个月'),
 77                             (120, '4个月'),
 78                             (180, '6个月'), (210, '12个月'),
 79                             (540, '18个月'), (720, '24个月'),
 80                             (722, '24个月'), (723, '24个月'),
 81                             )
 82     valid_period = models.SmallIntegerField(choices=valid_period_choices)
 83     price = models.FloatField()
 84 
 85     class Meta:
 86         unique_together = ("content_type", 'object_id', "valid_period")
 87 
 88     def __str__(self):
 89         return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price)
 90 
 91 class CourseChapter(models.Model):
 92     """课程章节"""
 93     course = models.ForeignKey("Course", related_name='coursechapters',on_delete=models.CASCADE)
 94     chapter = models.SmallIntegerField(verbose_name="第几章", default=1)
 95     name = models.CharField(max_length=128)
 96     summary = models.TextField(verbose_name="章节介绍", blank=True, null=True)
 97     is_create = models.BooleanField(verbose_name="是否创建题库进度", default=True)
 98     pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True)
 99 
100     class Meta:
101         unique_together = ("course", 'chapter')
102 
103     def __str__(self):
104         return "%s:(第%s章)%s" % (self.course, self.chapter, self.name)
105 
106 class CourseSection(models.Model):
107     """课时目录"""
108     chapter = models.ForeignKey("CourseChapter", related_name='coursesections',on_delete=models.CASCADE)
109     name = models.CharField(max_length=128)
110     order = models.PositiveSmallIntegerField(verbose_name="课时排序", help_text="建议每个课时之间空1至2个值,以备后续插入课时")
111     section_type_choices = ((0, '文档'), (1, '练习'), (2, '视频'))
112     section_type = models.SmallIntegerField(default=2, choices=section_type_choices)
113     section_link = models.CharField(max_length=255, blank=True, null=True, help_text="若是video,填vid,若是文档,填link")
114     video_time = models.CharField(verbose_name="视频时长", blank=True, null=True, max_length=32)  # 仅在前端展示使用
115     pub_date = models.DateTimeField(verbose_name="发布时间", auto_now_add=True)
116     free_trail = models.BooleanField("是否可试看", default=False)
117     is_flash = models.BooleanField(verbose_name="是否使用FLASH播放", default=False)
118     player_choices = ((0, "CC"), (1, "POLYV"), (2, "ALI"))
119     player = models.SmallIntegerField(choices=player_choices, default=1, help_text="视频播放器选择")
120 
121     def course_chapter(self):
122         return self.chapter.chapter
123 
124     def course_name(self):
125         return self.chapter.course.name
126 
127     class Meta:
128         unique_together = ('chapter', 'section_link')
129 
130     def __str__(self):
131         return "%s-%s" % (self.chapter, self.name)
132 
133 
134 class OftenAskedQuestion(models.Model):
135     """常见问题"""
136     content_type = models.ForeignKey(ContentType,limit_choices_to={'model__contains': 'course'},on_delete=models.CASCADE)  # 关联course or degree_course
137     object_id = models.PositiveIntegerField()
138     content_object = GenericForeignKey('content_type', 'object_id')
139 
140     question = models.CharField(max_length=255)
141     answer = models.TextField(max_length=1024)
142 
143     def __str__(self):
144         return "%s-%s" % (self.content_object, self.question)
145 
146     class Meta:
147         unique_together = ('content_type', 'object_id', 'question')
148         verbose_name_plural = "常见问题"
149 
150 ######################################## 优惠券 ########################################
151 
152 class Coupon(models.Model):
153     """优惠券生成规则"""
154     name = models.CharField(max_length=64, verbose_name="活动名称")
155     brief = models.TextField(blank=True, null=True, verbose_name="优惠券介绍")
156     coupon_type_choices = ((0, '立减券'), (1, '满减券'), (2, '折扣券'))
157     coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券类型")
158     money_equivalent_value = models.FloatField(verbose_name="等值货币")
159     off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True)
160     minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段")
161     content_type = models.ForeignKey(ContentType, blank=True, null=True,on_delete=models.CASCADE)
162     object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定")
163     content_object = GenericForeignKey('content_type', 'object_id')
164     quantity = models.PositiveIntegerField("数量(张)", default=1)
165     open_date = models.DateField("优惠券领取开始时间")
166     close_date = models.DateField("优惠券领取结束时间")
167     valid_begin_date = models.DateField(verbose_name="有效期开始时间", blank=True, null=True)
168     valid_end_date = models.DateField(verbose_name="有效结束时间", blank=True, null=True)
169     coupon_valid_days = models.PositiveIntegerField(verbose_name="优惠券有效期(天)", blank=True, null=True,
170                                                     help_text="自券被领时开始算起")
171     status_choices = ((0, "上线"), (1, "下线"))
172     status = models.SmallIntegerField(choices=status_choices, default=0)
173     date = models.DateTimeField(auto_now_add=True)
174 
175     def __str__(self):
176         return "%s(%s)" % (self.get_coupon_type_display(), self.name)
177 
178 
179 class CouponRecord(models.Model):
180     """优惠券发放、消费纪录"""
181     coupon = models.ForeignKey("Coupon",on_delete=models.CASCADE)
182     account = models.ForeignKey("UserInfo", blank=True, null=True, verbose_name="使用者",on_delete=models.CASCADE)
183     status_choices = ((0, '未使用'), (1, '已使用'), (2, '已过期'), (3, '未领取'))
184     status = models.SmallIntegerField(choices=status_choices, default=0)
185     get_time = models.DateTimeField(blank=True, null=True, verbose_name="领取时间", help_text="用户领取时间")
186     used_time = models.DateTimeField(blank=True, null=True, verbose_name="使用时间")
187     order = models.ForeignKey("Order", blank=True, null=True, verbose_name="关联订单",on_delete=models.CASCADE)  # 一个订单可以有多个优惠券
188     date = models.DateTimeField(auto_now_add=True, verbose_name="生成时间")
189     #_coupon = GenericRelation("Coupon")
190 
191     def __str__(self):
192         return self.coupon.name+"优惠券记录"
193 
194 ######################################## 订单表 ########################################
195 
196 class Order(models.Model):
197     """订单"""
198     payment_type_choices = ((0, '微信'), (1, '支付宝'), (2, '优惠码'), (3, '贝里'), (4, '银联'))
199     payment_type = models.SmallIntegerField(choices=payment_type_choices)
200     payment_number = models.CharField(max_length=128, verbose_name="支付第3方订单号", null=True, blank=True)
201     order_number = models.CharField(max_length=128, verbose_name="订单号", unique=True)  # 考虑到订单合并支付的问题
202     account = models.ForeignKey("UserInfo",on_delete=models.CASCADE)
203     actual_amount = models.FloatField(verbose_name="实付金额")
204     # coupon = models.OneToOneField("Coupon", blank=True, null=True, verbose_name="优惠码") #一个订单可以有多个优惠券
205     status_choices = ((0, '交易成功'), (1, '待支付'), (2, '退费申请中'), (3, '已退费'), (4, '主动取消'), (5, '超时取消'))
206     status = models.SmallIntegerField(choices=status_choices, verbose_name="状态")
207     order_type_choices = ((0, '用户下单'), (1, '线下班创建'),)
208     order_type = models.SmallIntegerField(choices=order_type_choices, default=0, verbose_name="订单类型")
209     date = models.DateTimeField(auto_now_add=True, verbose_name="订单生成时间")
210     pay_time = models.DateTimeField(blank=True, null=True, verbose_name="付款时间")
211     cancel_time = models.DateTimeField(blank=True, null=True, verbose_name="订单取消时间")
212 
213     def __str__(self):
214         return "%s" % self.order_number
215 
216 
217 class OrderDetail(models.Model):
218     """订单详情"""
219     order = models.ForeignKey("Order",on_delete=models.CASCADE)
220     content_type = models.ForeignKey(ContentType,on_delete=models.CASCADE)  # 可关联普通课程或学位
221     object_id = models.PositiveIntegerField()
222     content_object = GenericForeignKey('content_type', 'object_id')
223     original_price = models.FloatField("课程原价")
224     price = models.FloatField("折后价格")
225     content = models.CharField(max_length=255, blank=True, null=True)  #
226     valid_period_display = models.CharField("有效期显示", max_length=32)  # 在订单页显示
227     valid_period = models.PositiveIntegerField("有效期(days)")  # 课程有效期
228     memo = models.CharField(max_length=255, blank=True, null=True)
229 
230     def __str__(self):
231         return "%s - %s - %s" % (self.order, self.content_type, self.price)
232 
233     class Meta:
234         # unique_together = ("order", 'course')
235         unique_together = ("order", 'content_type', 'object_id')
236 
237 
238 ######################################## 用户表 ########################################
239 
240 from django.utils.safestring import mark_safe
241 
242 from django.contrib.auth.models import AbstractUser
243 
244 class UserInfo(AbstractUser):
245     username = models.CharField("用户名", max_length=64, unique=True)
246     email = models.EmailField(
247         verbose_name='email address',
248         max_length=255,
249         unique=True,
250         blank=True,
251         null=True
252     )
253     uid = models.CharField(max_length=64, unique=True)  # 与第3方交互用户信息时,用这个uid,以避免泄露敏感用户信息
254     mobile = models.BigIntegerField(verbose_name="手机", unique=True, help_text="用于手机验证码登录", null=True)
255     qq = models.CharField(verbose_name="QQ", max_length=64, blank=True, null=True, db_index=True)
256     weixin = models.CharField(max_length=128, blank=True, null=True, db_index=True, verbose_name="微信")
257     signature = models.CharField('个人签名', blank=True, null=True, max_length=255)
258     brief = models.TextField("个人介绍", blank=True, null=True)
259     openid = models.CharField(max_length=128, blank=True, null=True)
260     alipay_card = models.CharField(max_length=128, blank=True, null=True, verbose_name="支付宝账户")
261     gender_choices = ((0, '保密'), (1, ''), (2, ''))
262     gender = models.SmallIntegerField(choices=gender_choices, default=0, verbose_name="性别")
263     id_card = models.CharField(max_length=32, blank=True, null=True, verbose_name="身份证号或护照号")
264     password = models.CharField('password', max_length=128,
265                                 help_text=mark_safe('''<a class='btn-link' href='password'>重置密码</a>'''))
266     is_active = models.BooleanField(default=True, verbose_name="账户状态")
267     is_staff = models.BooleanField(verbose_name='staff status', default=False, help_text='决定着用户是否可登录管理后台')
268     name = models.CharField(max_length=32, default="", verbose_name="真实姓名")
269     head_img = models.CharField(max_length=256, default='/static/frontend/head_portrait/logo@2x.png',
270                                 verbose_name="个人头像")
271     role_choices = ((0, '学员'), (1, '导师'), (2, '讲师'), (3, '管理员'), (4, '班主任'), (5, '线下班主任'))
272     role = models.SmallIntegerField(choices=role_choices, default=0, verbose_name="角色")
273     # balance = models.PositiveIntegerField(default=0, verbose_name="可提现余额")
274     # #此处通过transaction_record表就可以查到,所以不用写在这了
275     memo = models.TextField('备注', blank=True, null=True, default=None, help_text="json格式存储")
276     date_joined = models.DateTimeField(auto_now_add=True, verbose_name="注册时间")
277 
278     class Meta:
279         verbose_name = '账户信息'
280         verbose_name_plural = "账户信息"
281 
282 
283     def save(self, *args, **kwargs):
284         if not self.pk:
285             import hashlib
286             # This code only happens if the objects is not in the database yet. Otherwise it would have pk
287             m = hashlib.md5()
288             m.update(self.username.encode(encoding="utf-8"))
289             self.uid = m.hexdigest()
290         super(UserInfo, self).save(*args, **kwargs)
291 
292 
293     def __str__(self):
294         return "%s(%s)" % (self.username, self.get_role_display())
295 
296 
297 class Token(models.Model):
298     """
299     The default authorization token model.
300     """
301     key = models.CharField(max_length=40)
302     user = models.OneToOneField(
303         UserInfo, related_name='auth_token',
304         on_delete=models.CASCADE, verbose_name="关联用户"
305     )
306     created = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
307 
308 
309     def __str__(self):
310         return self.key
311 
312 
313 ##############################################################
314 
315 
316 class Student(models.Model):
317      name=models.CharField(max_length=32)
318      age=models.IntegerField()
319      gender=models.IntegerField()
View Code

在同步表的时候遇到一个问题,报错userinfo表,是因为Django本身会提供一张user表,我们写的userinfo是在user表的基础上扩展字段,所以要在settings中进行如下配置

AUTH_USER_MODEL="app01.UserInfo"   #app01代表创建的app名

这样再同步就没有问题了