def test_no_index_for_foreignkey(self): """ MySQL on InnoDB already creates indexes automatically for foreign keys. (#14180). An index should be created if db_constraint=False (#26171). """ storage = connection.introspection.get_storage_engine( connection.cursor(), ArticleTranslation._meta.db_table ) if storage != "InnoDB": self.skip("This test only applies to the InnoDB storage engine") index_sql = [str(statement) for statement in connection.schema_editor()._model_indexes_sql(ArticleTranslation)] self.assertEqual(index_sql, [ 'CREATE INDEX `indexes_articletranslation_article_no_constraint_id_d6c0806b` ' 'ON `indexes_articletranslation` (`article_no_constraint_id`)' ]) # The index also shouldn't be created if the ForeignKey is added after # the model was created. field_created = False try: with connection.schema_editor() as editor: new_field = ForeignKey(Article, CASCADE) new_field.set_attributes_from_name('new_foreign_key') editor.add_field(ArticleTranslation, new_field) field_created = True # No deferred SQL. The FK constraint is included in the # statement to add the field. self.assertFalse(editor.deferred_sql) finally: if field_created: with connection.schema_editor() as editor: editor.remove_field(ArticleTranslation, new_field)
def test_alter_fk(self): """ Tests altering of FKs """ # Create the table with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) # Ensure the field is right to begin with columns = self.column_classes(Book) self.assertEqual(columns["author_id"][0], "IntegerField") # Make sure the FK constraint is present constraints = self.get_constraints(Book._meta.db_table) for name, details in constraints.items(): if details["columns"] == ["author_id"] and details["foreign_key"]: self.assertEqual(details["foreign_key"], ("schema_author", "id")) break else: self.fail("No FK constraint for author_id found") # Alter the FK new_field = ForeignKey(Author, editable=False) new_field.set_attributes_from_name("author") with connection.schema_editor() as editor: editor.alter_field(Book, Book._meta.get_field_by_name("author")[0], new_field, strict=True) # Ensure the field is right afterwards columns = self.column_classes(Book) self.assertEqual(columns["author_id"][0], "IntegerField") # Make sure the FK constraint is present constraints = self.get_constraints(Book._meta.db_table) for name, details in constraints.items(): if details["columns"] == ["author_id"] and details["foreign_key"]: self.assertEqual(details["foreign_key"], ("schema_author", "id")) break else: self.fail("No FK constraint for author_id found")
def test_fk(self): "Tests that creating tables out of FK order, then repointing, works" # Create the table with connection.schema_editor() as editor: editor.create_model(Book) editor.create_model(Author) editor.create_model(Tag) # Check that initial tables are there list(Author.objects.all()) list(Book.objects.all()) # Make sure the FK constraint is present with self.assertRaises(IntegrityError): Book.objects.create( author_id=1, title="Much Ado About Foreign Keys", pub_date=datetime.datetime.now(), ) # Repoint the FK constraint new_field = ForeignKey(Tag) new_field.set_attributes_from_name("author") with connection.schema_editor() as editor: editor.alter_field( Book, Book._meta.get_field_by_name("author")[0], new_field, strict=True, ) # Make sure the new FK constraint is present constraints = self.get_constraints(Book._meta.db_table) for name, details in constraints.items(): if details['columns'] == ["author_id"] and details['foreign_key']: self.assertEqual(details['foreign_key'], ('schema_tag', 'id')) break else: self.fail("No FK constraint for author_id found")
def test_no_index_for_foreignkey(self): """ MySQL on InnoDB already creates indexes automatically for foreign keys. (#14180). An index should be created if db_constraint=False (#26171). """ storage = connection.introspection.get_storage_engine( connection.cursor(), ArticleTranslation._meta.db_table ) if storage != "InnoDB": self.skip("This test only applies to the InnoDB storage engine") index_sql = connection.schema_editor()._model_indexes_sql(ArticleTranslation) self.assertEqual(index_sql, [ 'CREATE INDEX `indexes_articletranslation_article_no_constraint_id_d6c0806b` ' 'ON `indexes_articletranslation` (`article_no_constraint_id`)' ]) # The index also shouldn't be created if the ForeignKey is added after # the model was created. with connection.schema_editor() as editor: new_field = ForeignKey(Article, CASCADE) new_field.set_attributes_from_name('new_foreign_key') editor.add_field(ArticleTranslation, new_field) self.assertEqual(editor.deferred_sql, [ 'ALTER TABLE `indexes_articletranslation` ' 'ADD CONSTRAINT `indexes_articletrans_new_foreign_key_id_d27a9146_fk_indexes_a` ' 'FOREIGN KEY (`new_foreign_key_id`) REFERENCES `indexes_article` (`id`)' ])
def test_fk_index_creation(self): new_field = ForeignKey(Foo) new_field.set_attributes_from_name(None) with connection.schema_editor() as editor: editor.add_field( Bar, new_field ) # Just return indexes others that not automaically created by Fk indexes = editor._get_field_indexes(Bar, new_field) self.assertEqual(indexes, [])
def __init__(self, to, chained_field=None, chained_model_field=None, show_all=False, auto_choose=False, **kwargs): if isinstance(to, basestring): self.app_name, self.model_name = to.split('.') else: self.app_name = to._meta.app_label self.model_name = to._meta.object_name self.chain_field = chained_field self.model_field = chained_model_field self.show_all = show_all self.auto_choose = auto_choose ForeignKey.__init__(self, to, **kwargs)
def _register(model, field_name, related_name, model_registry, model_map): """ Set up the foreign keys on the `Action` model """ if model in model_registry: return model_registry.append(model) field = ForeignKey(model, related_name=related_name, blank=True, null=True, db_index=True) field.contribute_to_class(Action, field_name) model_map[model] = [related_name, field_name]
def __init__(self, to, address_field=None, **kwargs): if isinstance(to, six.string_types): self.app_name, self.model_name = to.split('.') else: self.app_name = to._meta.app_label self.model_name = to._meta.object_name self.address_field = address_field kwargs.setdefault('blank', True) kwargs.setdefault('null', True) ForeignKey.__init__(self, to, **kwargs)
def test_add_foreign_key_long_names(self): """ Regression test for #23009. Only affects databases that supports foreign keys. """ # Create the initial tables with connection.schema_editor() as editor: editor.create_model(AuthorWithEvenLongerName) editor.create_model(BookWithLongName) # Add a second FK, this would fail due to long ref name before the fix new_field = ForeignKey(AuthorWithEvenLongerName, related_name="something") new_field.set_attributes_from_name("author_other_really_long_named_i_mean_so_long_fk") with connection.schema_editor() as editor: editor.add_field(BookWithLongName, new_field)
def __init__(self, to, chained_field=None, chained_model_field=None, show_all=False, auto_choose=False, view_name=None, exclude_self=None, **kwargs): if isinstance(to, basestring): self.app_name, self.model_name = to.split('.') else: self.app_name = to._meta.app_label self.model_name = to._meta.object_name self.chain_field = chained_field self.model_field = chained_model_field self.show_all = show_all self.auto_choose = auto_choose self.view_name = view_name ForeignKey.__init__(self, to, **kwargs) self.exclude_self = exclude_self #TODO: clean-up if exclude_self: self.exclude_self = exclude_self
def __init__(self, to, chained_field=None, chained_model_field=None, show_all=False, auto_choose=False, view_name=None, **kwargs): """ examples: class Continent(models.Model): name = models.CharField(max_length=255) class Country(models.Model): continent = models.ForeignKey(Continent) class Location(models.Model): continent = models.ForeignKey(Continent) country = ChainedForeignKey( Country, chained_field="continent", chained_model_field="continent", show_all=True, auto_choose=True, # limit_choices_to={'name':'test'} ) ``chained_field`` is the name of the ForeignKey field referenced by ChainedForeignKey of the same Model. in the examples, chained_field is the name of field continent in Model Location. ``chained_model_field`` is the name of the ForeignKey field referenced in the 'to' Model. in the examples, chained_model_field is the name of field continent in Model Country. ``show_all`` controls whether show other choices below the filtered choices, with separater '----------'. ``auto_choose`` controls whether auto select the choice when there is only one available choice. ``view_name`` controls which view to use, 'chained_filter' or 'chained_filter_all'. """ if isinstance(to, six.string_types): self.to_app_name, self.to_model_name = to.split('.') else: self.to_app_name = to._meta.app_label self.to_model_name = to._meta.object_name self.chained_field = chained_field self.chained_model_field = chained_model_field self.show_all = show_all self.auto_choose = auto_choose self.view_name = view_name ForeignKey.__init__(self, to, **kwargs)
def test_fk_db_constraint(self): "Tests that the db_constraint parameter is respected" # Create the table with connection.schema_editor() as editor: editor.create_model(Tag) editor.create_model(Author) editor.create_model(BookWeak) # Check that initial tables are there list(Author.objects.all()) list(Tag.objects.all()) list(BookWeak.objects.all()) # Check that BookWeak doesn't have an FK constraint constraints = self.get_constraints(BookWeak._meta.db_table) for name, details in constraints.items(): if details["columns"] == ["author_id"] and details["foreign_key"]: self.fail("FK constraint for author_id found") # Make a db_constraint=False FK new_field = ForeignKey(Tag, db_constraint=False) new_field.set_attributes_from_name("tag") with connection.schema_editor() as editor: editor.add_field(Author, new_field) # Make sure no FK constraint is present constraints = self.get_constraints(Author._meta.db_table) for name, details in constraints.items(): if details["columns"] == ["tag_id"] and details["foreign_key"]: self.fail("FK constraint for tag_id found") # Alter to one with a constraint new_field_2 = ForeignKey(Tag) new_field_2.set_attributes_from_name("tag") with connection.schema_editor() as editor: editor.alter_field(Author, new_field, new_field_2, strict=True) # Make sure the new FK constraint is present constraints = self.get_constraints(Author._meta.db_table) for name, details in constraints.items(): if details["columns"] == ["tag_id"] and details["foreign_key"]: self.assertEqual(details["foreign_key"], ("schema_tag", "id")) break else: self.fail("No FK constraint for tag_id found") # Alter to one without a constraint again new_field_2 = ForeignKey(Tag) new_field_2.set_attributes_from_name("tag") with connection.schema_editor() as editor: editor.alter_field(Author, new_field_2, new_field, strict=True) # Make sure no FK constraint is present constraints = self.get_constraints(Author._meta.db_table) for name, details in constraints.items(): if details["columns"] == ["tag_id"] and details["foreign_key"]: self.fail("FK constraint for tag_id found")
def register(model, field_name=None, related_name=None, lookup_method_name='get_follows'): """ This registers any model class to be follow-able. """ if model in registry: return registry.append(model) if not field_name: field_name = 'target_%s' % model._meta.module_name if not related_name: related_name = 'follow_%s' % model._meta.module_name field = ForeignKey(model, related_name=related_name, null=True, blank=True, db_index=True) field.contribute_to_class(Follow, field_name) setattr(model, lookup_method_name, get_followers_for_object) model_map[model] = [related_name, field_name]
def __init__(self, to='fias.AddrObj', **kwargs): ForeignKey.__init__(self, to, **kwargs)
class Video(models.Model): name = models.CharField(max_length=255) video = models.FileField(storage=fs) uploader = ForeignKey(User, null=False, blank=False, on_delete=models.CASCADE) thumbnail = models.FileField(storage=thumbnails, null=True) date_uploaded = models.DateField(auto_now=True) #Resolution resolution = models.CharField(max_length=64, blank=True, null=True, default="Unknown") resolution_rating = models.CharField(max_length=64, blank=True, null=True, default="Unknown") resolution_recommended = models.CharField(max_length=64, blank=True, null=True, default="Unknown") resolution_recommendation = models.TextField( blank=True, null=True, default="No recommendation available.") #Shutter speed #shutter_speed = models.CharField(max_length=64, blank=True, null=True, default="Unknown") #shutter_speed_rating = models.CharField(max_length=64, blank=True, null=True, default="Unknown") #shutter_speed_recommendation = models.TextField(blank=True, null=True, default="No recommendation available.") #Frame rate frame_rate = models.CharField(max_length=64, blank=True, null=True, default="Unknown") frame_rate_rating = models.CharField(max_length=64, blank=True, null=True, default="Unknown") frame_rate_recommended = models.CharField(max_length=64, blank=True, null=True, default="Unknown") frame_rate_recommendation = models.TextField( blank=True, null=True, default="No recommendation available.") #Bit rate bit_rate = models.CharField(max_length=64, blank=True, null=True, default="Unknown") bit_rate_rating = models.CharField(max_length=64, blank=True, null=True, default="Unknown") bit_rate_recommended = models.CharField(max_length=64, blank=True, null=True, default="Unknown") bit_rate_recommendation = models.TextField( blank=True, null=True, default="No recommendation available.") #Bit depth bit_depth = models.CharField(max_length=64, blank=True, null=True, default="Unknown") bit_depth_rating = models.CharField(max_length=64, blank=True, null=True, default="Unknown") bit_depth_recommended = models.CharField(max_length=64, blank=True, null=True, default="Unknown") bit_depth_recommendation = models.TextField( blank=True, null=True, default="No recommendation available.") #Sample rate sample_rate = models.CharField(max_length=64, blank=True, null=True, default="Unknown") sample_rate_rating = models.CharField(max_length=64, blank=True, null=True, default="Unknown") sample_rate_recommended = models.CharField(max_length=64, blank=True, null=True, default="Unknown") sample_rate_recommendation = models.TextField( blank=True, null=True, default="No recommendation available.") #Video length video_length = models.CharField(max_length=64, blank=True, null=True, default="Unknown") video_length_rating = models.CharField(max_length=64, blank=True, null=True, default="Unknown") video_length_recommended = models.CharField(max_length=64, blank=True, null=True, default="Unknown") video_length_recommendation = models.TextField( blank=True, null=True, default="No recommendation available.") def __str__(self): return '%s' % (self.name) # Returns actual file name. It should assist in dealing with videos with the same name. def filename(self): return os.path.basename(self.video.name)
def __init__(self, translated_field, language, to, to_field=None, *args, **kwargs): self._related_pre_init(translated_field, language, *args, **kwargs) ForeignKey.__init__(self, to, to_field, **kwargs) self._related_post_init()
class DocumentAttach(Attach): file = ForeignKey(Document, on_delete=models.CASCADE) def attaches_from(self, obj): return obj.documents
def __init__(self, to, chained_field, chained_model_field, *args, **kwargs): self.app_name = to._meta.app_label self.model_name = to._meta.object_name self.chain_field = chained_field self.model_field = chained_model_field ForeignKey.__init__(self, to, *args, **kwargs)
class Grades(BaseModel): test1 = models.FloatField('Test 1') test2 = models.FloatField('Test 2') project = models.FloatField('Project') student_id = ForeignKey(Student, on_delete=models.RESTRICT) subject_id = ForeignKey(Subject, on_delete=models.RESTRICT)
class Plan(StripePlan): __doc__ = getattr(StripePlan, "__doc__") account = ForeignKey("Account", on_delete=models.CASCADE, related_name="plans", null=True) class Meta(object): ordering = ["amount"] @classmethod def get_or_create(cls, **kwargs): """ Get or create a Plan.""" try: return Plan.objects.get(account=kwargs['account'], stripe_id=kwargs['stripe_id']), False except Plan.DoesNotExist: return cls.create(**kwargs), True @classmethod def create(cls, **kwargs): # A few minor things are changed in the api-version of the create call api_kwargs = dict(kwargs) api_kwargs['id'] = api_kwargs['stripe_id'] del (api_kwargs['stripe_id']) api_kwargs['amount'] = int(api_kwargs['amount'] * 100) account = api_kwargs['account'] stripe_account = account.stripe_id del (api_kwargs['account']) api_kwargs['stripe_account'] = stripe_account cls._api_create(**api_kwargs) plan = Plan.objects.create(**kwargs) return plan @property def human_readable_price(self): amount = get_friendly_currency_amount(self.amount, self.currency) interval_count = self.interval_count if interval_count == 1: interval = self.interval template = "{amount}/{interval}" else: interval = { "day": "days", "week": "weeks", "month": "months", "year": "years" }[self.interval] template = "{amount} every {interval_count} {interval}" return template.format(amount=amount, interval=interval, interval_count=interval_count) # TODO: Move this type of update to the model's save() method so it happens automatically # Also, block other fields from being saved. def update_name(self): """Update the name of the Plan in Stripe and in the db. - Assumes the object being called has the name attribute already reset, but has not been saved. - Stripe does not allow for update of any other Plan attributes besides name. """ p = self.api_retrieve() p.name = self.name p.save() self.save()
class Section(models.Model): name = CharField(max_length=255) ensemble = ForeignKey(Ensemble) def __unicode__(self): return "%s %s: %s" % (self.__class__.__name__, self.id, self.name)
class Reminder(models.Model): project_id = CharField(null=False, blank=False, max_length=264) day = IntegerField(null=True, blank=True) message = CharField(max_length=160) reminder_mode = CharField(null=False, blank=False, max_length=20, default=ReminderMode.BEFORE_DEADLINE) organization = ForeignKey(Organization) def to_dict(self): return { 'day': self.day, 'message': self.message, 'reminder_mode': self.reminder_mode, 'id': self.id } def void(self, void=True): self.voided = void self.save() def should_be_send_on(self, deadline, on_date): assert isinstance(on_date, date) deadline_date = self._get_applicapable_deadline_date(deadline, on_date) return on_date == deadline_date + timedelta(days=self._delta()) def get_sender_list(self, project, on_date, dbm): if not project.reminder_and_deadline['should_send_reminder_to_all_ds']: deadline_date = self._get_applicapable_deadline_date( project.deadline(), on_date) return project.get_data_senders_without_submissions_for( deadline_date, dbm, project._frequency_period()) return project.get_data_senders(dbm) def _delta(self): if self.reminder_mode == ReminderMode.ON_DEADLINE: return 0 if self.reminder_mode == ReminderMode.BEFORE_DEADLINE: return -self.day if self.reminder_mode == ReminderMode.AFTER_DEADLINE: return self.day def _get_applicapable_deadline_date(self, deadline, on_date): if self.reminder_mode == ReminderMode.BEFORE_DEADLINE: return deadline.next_deadline(on_date) else: return deadline.current_deadline(on_date) def log(self, dbm, project_id, date, to_number, sent_status='sent', number_of_sms=0): log = ReminderLog(dbm=dbm, reminder=self, project_id=project_id, date=date, sent_status=sent_status, number_of_sms=number_of_sms, to_number=to_number) log.save() return log
class SavedListing(models.Model): applicant = ForeignKey(Applicant, on_delete=CASCADE) listing = ForeignKey(Listing, on_delete=CASCADE) def __str__(self): return str(self.applicant) + ' ' + str(self.listing)
class Organisation(models.Model): name = models.CharField('name', max_length=30) image = models.ImageField(upload_to=get_organisation_logo_path, blank=True, default='default.jpg') primary_contact = ForeignKey(Contact, on_delete=models.CASCADE) address = models.ForeignKey(Address, on_delete=models.CASCADE) description = models.TextField('description') just_giving_link = models.URLField('just giving link', max_length=255, blank=True) raised = models.DecimalField( 'raised', max_digits=25, decimal_places=2, blank=True, null=True, validators=[MinValueValidator(0, 'Raised cannot be negative')]) goal = models.DecimalField( 'goal', max_digits=25, decimal_places=2, blank=True, null=True, validators=[MinValueValidator(0, 'Goal cannot be negative')]) def __str__(self): return str(self.id) + " " + self.name def image_preview_large(self): if self.image: return format_html('<img src="{}" width="150" height="150"/>', self.image.url) return 'No Logo' image_preview_large.short_description = 'Image Preview' def image_preview_small(self): if self.image: return format_html('<img src="{}" width="50" height="50"/>', self.image.url) return 'No Logo' image_preview_small.short_description = 'Image Preview' def associated_user_accounts(self): if not self.organisationuser_set.count(): return 'No Accounts' return ','.join( str(item.user.id) + ': ' + item.user.username for item in self.organisationuser_set.all()) associated_user_accounts.short_description = 'User Accounts' def percentage_to_fund_raising_goal(self): if not (self.raised and self.goal): return 0 val = int(round(self.raised / self.goal * 100)) if val > 100: return 100 return val
class PostFile(models.Model): post = ForeignKey(Post, on_delete=models.CASCADE) #FileField: 모든 파일을 저장할수 있는 저장공간(한글문서,실행파일,이미지파일 등) file = models.FileField('첨부파일', upload_to='files/%Y/%m/%d')
class DemoCartItem(CartItem): cart = ForeignKey(DemoCart, related_name='items')
class GamePicture(models.Model): user = ForeignKey("User", on_delete=CASCADE) game = ForeignKey("Game", on_delete=CASCADE) image = models.ImageField(upload_to='images/', verbose_name="")
class BookCard(models.Model): name = models.CharField( verbose_name='Book name', max_length=80 ) picture = models.ImageField( verbose_name='Photo', upload_to='uploads/', null=True, blank=True ) cost = models.DecimalField( verbose_name='Cost [USD]', max_digits=10, decimal_places=2, default=0.0 ) authors = ManyToManyField( 'references.Authors', verbose_name='Authors', blank=True ) series = ForeignKey( 'references.Series', on_delete = models.PROTECT, blank=True, null=True ) genres = ManyToManyField( 'references.Genres', verbose_name='Genres', blank=True ) year = models.SmallIntegerField( verbose_name='Year', default=1900 ) num_pages = models.SmallIntegerField( verbose_name='Pages amount', default=0 ) # book_format = ForeignKey( # 'references.BookFormats', # on_delete = models.PROTECT, # blank=True, # null=True # ) # isbn = models.CharField( # verbose_name='ISBN', # max_length=13, # blank=True, # null=True # ) # weight = models.SmallIntegerField( # verbose_name='Вес (гр)', # default=0 # ) age_category = ForeignKey( 'references.AgeCategories', on_delete = models.PROTECT, blank=True, null=True ) publisher = ForeignKey( 'references.Publishers', on_delete = models.PROTECT, blank=True, null=True ) qty = models.SmallIntegerField( verbose_name='Amount', default=0 ) # is_active = models.BooleanField( # verbose_name='available to order', # default=True # ) rating = models.FloatField( verbose_name='Rating', default=0.0 ) create_date = models.DateTimeField( verbose_name='Date of creation', auto_now_add=True ) update_date = models.DateTimeField( verbose_name='Date of last update', auto_now=True ) def get_authors(self): return "\n".join([p.name for p in self.authors.all()]) def get_genres(self): return "\n".join([p.name for p in self.genres.all()]) def __str__(self): return self.name
class Session(models.Model): user = ForeignKey(User) ctime = DateTimeField(default=datetime.now) lastactivity = DateTimeField(default=datetime.now, null=True) ip = CharField(max_length=63, blank=True, null=True) clienttime = DateTimeField(blank=True, null=True)
class GuardianStudentBridge(BaseModel): guardian_id = ForeignKey(Guardian, on_delete=models.RESTRICT) student_id = ForeignKey(Student, on_delete=models.RESTRICT) def __str__(self) -> str: return f'{self.guardian_id} - {self.student_id}'
class CommentSeen(models.Model): comment = ForeignKey(Comment) session = ForeignKey(Session, null=True) user = ForeignKey( User) #duplicate (cf session) but inlined for performance ctime = DateTimeField(default=datetime.now)
def __init__(self, to, group_field, **kwargs): self.group_field = group_field self._choices = True ForeignKey.__init__(self, to, **kwargs)
class Idle(models.Model): session = ForeignKey(Session) t1 = DateTimeField() t2 = DateTimeField()
class Customer(StripeCustomer): doc = """ .. note:: Sources and Subscriptions are attached via a ForeignKey on StripeSource and Subscription, respectively. \ Use ``Customer.sources`` and ``Customer.subscriptions`` to access them. """ __doc__ = getattr(StripeCustomer, "__doc__") + doc # account = ForeignKey(Account, related_name="customers") default_source = ForeignKey(StripeSource, null=True, related_name="customers", on_delete=SET_NULL) subscriber = ForeignKey(djstripe_settings.get_subscriber_model_string(), null=True, on_delete=SET_NULL, related_name="djstripe_customers") date_purged = DateTimeField(null=True, editable=False) djstripe_subscriber_key = "djstripe_subscriber" class Meta: unique_together = ("subscriber", "livemode") def str_parts(self): parts = [] if self.subscriber: parts.append(smart_text(self.subscriber)) parts.append("email={email}".format(email=self.subscriber.email)) else: parts.append("(deleted)") parts.extend(super(Customer, self).str_parts()) return parts @classmethod def get_or_create(cls, subscriber, livemode=djstripe_settings.STRIPE_LIVE_MODE): """ Get or create a dj-stripe customer. :param subscriber: The subscriber model instance for which to get or create a customer. :type subscriber: User :param livemode: Whether to get the subscriber in live or test mode. :type livemode: bool """ try: return Customer.objects.get(subscriber=subscriber, livemode=livemode), False except Customer.DoesNotExist: action = "create:{}".format(subscriber.pk) idempotency_key = djstripe_settings.get_idempotency_key( "customer", action, livemode) return cls.create(subscriber, idempotency_key=idempotency_key), True @classmethod def create(cls, subscriber, idempotency_key=None): trial_days = None if djstripe_settings.trial_period_for_subscriber_callback: trial_days = djstripe_settings.trial_period_for_subscriber_callback( subscriber) stripe_customer = cls._api_create( email=subscriber.email, idempotency_key=idempotency_key, metadata={cls.djstripe_subscriber_key: subscriber.pk}) customer, created = Customer.objects.get_or_create( stripe_id=stripe_customer["id"], defaults={ "subscriber": subscriber, "livemode": stripe_customer["livemode"] }) if djstripe_settings.DEFAULT_PLAN and trial_days: customer.subscribe(plan=djstripe_settings.DEFAULT_PLAN, trial_end=timezone.now() + timezone.timedelta(days=trial_days)) return customer def purge(self): try: self._api_delete() except InvalidRequestError as exc: if "No such customer:" in str(exc): # The exception was thrown because the stripe customer was already # deleted on the stripe side, ignore the exception pass else: # The exception was raised for another reason, re-raise it six.reraise(*sys.exc_info()) self.subscriber = None # Remove sources self.default_source = None for source in self.sources.all(): source.remove() self.date_purged = timezone.now() self.save() # TODO: Override Queryset.delete() with a custom manager, since this doesn't get called in bulk deletes # (or cascades, but that's another matter) def delete(self, using=None, keep_parents=False): """ Overriding the delete method to keep the customer in the records. All identifying information is removed via the purge() method. The only way to delete a customer is to use SQL. """ self.purge() def _get_valid_subscriptions(self): """ Get a list of this customer's valid subscriptions.""" return [ subscription for subscription in self.subscriptions.all() if subscription.is_valid() ] def has_active_subscription(self, plan=None): """ Checks to see if this customer has an active subscription to the given plan. :param plan: The plan for which to check for an active subscription. If plan is None and there exists only one active subscription, this method will check if that subscription is valid. Calling this method with no plan and multiple valid subscriptions for this customer will throw an exception. :type plan: Plan or string (plan ID) :returns: True if there exists an active subscription, False otherwise. :throws: TypeError if ``plan`` is None and more than one active subscription exists for this customer. """ if plan is None: valid_subscriptions = self._get_valid_subscriptions() if len(valid_subscriptions) == 0: return False elif len(valid_subscriptions) == 1: return True else: raise TypeError( "plan cannot be None if more than one valid subscription exists for this customer." ) else: # Convert Plan to stripe_id if isinstance(plan, Plan): plan = plan.stripe_id return any([ subscription.is_valid() for subscription in self.subscriptions.filter( plan__stripe_id=plan) ]) def has_any_active_subscription(self): """ Checks to see if this customer has an active subscription to any plan. :returns: True if there exists an active subscription, False otherwise. :throws: TypeError if ``plan`` is None and more than one active subscription exists for this customer. """ return len(self._get_valid_subscriptions()) != 0 @property def active_subscriptions(self): """Returns active subscriptions (subscriptions with an active status that end in the future).""" return self.subscriptions.filter( status=StripeSubscription.STATUS_ACTIVE, current_period_end__gt=timezone.now()) @property def valid_subscriptions(self): """Returns this cusotmer's valid subscriptions (subscriptions that aren't cancelled.""" return self.subscriptions.exclude( status=StripeSubscription.STATUS_CANCELED) @property def subscription(self): """ Shortcut to get this customer's subscription. :returns: None if the customer has no subscriptions, the subscription if the customer has a subscription. :raises MultipleSubscriptionException: Raised if the customer has multiple subscriptions. In this case, use ``Customer.subscriptions`` instead. """ subscriptions = self.valid_subscriptions if subscriptions.count() > 1: raise MultipleSubscriptionException( "This customer has multiple subscriptions. Use Customer.subscriptions " "to access them.") else: return subscriptions.first() # TODO: Accept a coupon object when coupons are implemented def subscribe(self, plan, account=None, charge_immediately=True, **kwargs): # Convert Plan to stripe_id if isinstance(plan, Plan): plan = plan.stripe_id # Convert Account to stripe_id if isinstance(account, Account): account = account.stripe_id stripe_subscription = super(Customer, self).subscribe(plan=plan, account=account, **kwargs) #if charge_immediately: # self.send_invoice() return Subscription.sync_from_stripe_data(stripe_subscription) def can_charge(self): """Determines if this customer is able to be charged.""" return self.has_valid_source() and self.date_purged is None def charge(self, amount, currency="usd", **kwargs): stripe_charge = super(Customer, self).charge(amount=amount, currency=currency, **kwargs) charge = Charge.sync_from_stripe_data(stripe_charge) return charge def add_invoice_item(self, amount, currency, **kwargs): # Convert Invoice to stripe_id if "invoice" in kwargs and isinstance(kwargs["invoice"], Invoice): kwargs.update({"invoice": kwargs["invoice"].stripe_id}) # Convert Subscription to stripe_id if "subscription" in kwargs and isinstance(kwargs["subscription"], Subscription): kwargs.update({"subscription": kwargs["subscription"].stripe_id}) stripe_invoiceitem = super(Customer, self).add_invoice_item(amount=amount, currency=currency, **kwargs) return InvoiceItem.sync_from_stripe_data(stripe_invoiceitem) def send_invoice(self): """ Pay and send the customer's latest invoice. :returns: True if an invoice was able to be created and paid, False otherwise (typically if there was nothing to invoice). """ try: invoice = Invoice._api_create(customer=self.stripe_id) invoice.pay() return True except InvalidRequestError: # TODO: Check this for a more specific error message. return False # There was nothing to invoice def retry_unpaid_invoices(self): """ Attempt to retry collecting payment on the customer's unpaid invoices.""" self._sync_invoices() for invoice in self.invoices.filter(paid=False, closed=False): try: invoice.retry() # Always retry unpaid invoices except InvalidRequestError as exc: if str(exc) != "Invoice is already paid": six.reraise(*sys.exc_info()) def has_valid_source(self): """ Check whether the customer has a valid payment source.""" return self.default_source is not None def add_card(self, source, set_default=True): new_stripe_card = super(Customer, self).add_card(source, set_default) new_card = Card.sync_from_stripe_data(new_stripe_card) # Change the default source if set_default: self.default_source = new_card self.save() return new_card def upcoming_invoice(self, **kwargs): """ Gets the upcoming preview invoice (singular) for this customer. See `Invoice.upcoming() <#djstripe.Invoice.upcoming>`__. The ``customer`` argument to the ``upcoming()`` call is automatically set by this method. """ kwargs['customer'] = self return Invoice.upcoming(**kwargs) def _attach_objects_post_save_hook(self, cls, data): default_source = data.get("default_source") if default_source: # TODO: other sources if not isinstance(default_source, dict) or default_source.get("object") == "card": source, created = Card._get_or_create_from_stripe_object( data, "default_source", refetch=False) else: logger.warn("Unsupported source type on %r: %r", self, default_source) source = None if source and source != self.default_source: self.default_source = source self.save() def _attach_objects_hook(self, cls, data): # When we save a customer to Stripe, we add a reference to its Django PK # in the `django_account` key. If we find that, we re-attach that PK. subscriber_id = data.get("metadata", {}).get(self.djstripe_subscriber_key) if subscriber_id: cls = djstripe_settings.get_subscriber_model() try: # We have to perform a get(), instead of just attaching the PK # blindly as the object may have been deleted or not exist. # Attempting to save that would cause an IntegrityError. self.subscriber = cls.objects.get(pk=subscriber_id) except (cls.DoesNotExist, ValueError): logger.warn( "Could not find subscriber %r matching customer %r" % (subscriber_id, self.stripe_id)) self.subscriber = None # SYNC methods should be dropped in favor of the master sync infrastructure proposed def _sync_invoices(self, **kwargs): for stripe_invoice in Invoice.api_list(customer=self.stripe_id, **kwargs): Invoice.sync_from_stripe_data(stripe_invoice) def _sync_charges(self, **kwargs): for stripe_charge in Charge.api_list(customer=self.stripe_id, **kwargs): Charge.sync_from_stripe_data(stripe_charge) def _sync_cards(self, **kwargs): for stripe_card in Card.api_list(customer=self, **kwargs): Card.sync_from_stripe_data(stripe_card) def _sync_subscriptions(self, **kwargs): for stripe_subscription in Subscription.api_list( customer=self.stripe_id, status="all", **kwargs): Subscription.sync_from_stripe_data(stripe_subscription)
class Charge(StripeCharge): __doc__ = getattr(StripeCharge, "__doc__") account = ForeignKey( "Account", on_delete=models.CASCADE, null=True, related_name="charges", help_text= "The account the charge was made on behalf of. Null here indicates that this value was never set." ) customer = ForeignKey( "Customer", on_delete=models.CASCADE, null=True, related_name="charges", help_text="The customer associated with this charge.") transfer = ForeignKey( "Transfer", null=True, on_delete=models.CASCADE, help_text= "The transfer to the destination account (only applicable if the charge was created using the " "destination parameter).") source = ForeignKey(StripeSource, null=True, related_name="charges", on_delete=SET_NULL, help_text="The source used for this charge.") receipt_sent = BooleanField( default=False, help_text="Whether or not a receipt was sent for this charge.") objects = ChargeManager() def refund(self, amount=None, reason=None): refunded_charge = super(Charge, self).refund(amount, reason) return Charge.sync_from_stripe_data(refunded_charge) def capture(self): captured_charge = super(Charge, self).capture() return Charge.sync_from_stripe_data(captured_charge) def _attach_objects_hook(self, cls, data): customer = cls._stripe_object_to_customer(target_cls=Customer, data=data) if customer: self.customer = customer transfer = cls._stripe_object_to_transfer(target_cls=Transfer, data=data) if transfer: self.transfer = transfer # Set the account on this object. destination_account = cls._stripe_object_destination_to_account( target_cls=Account, data=data) if destination_account: self.account = destination_account else: self.account = Account.get_default_account() # TODO: other sources if self.source_type == "card": self.source = cls._stripe_object_to_source(target_cls=Card, data=data)
class ImageAttach(Attach): file = ForeignKey(Image, on_delete=models.CASCADE) def attaches_from(self, obj): return obj.images
class Event(StripeEvent): __doc__ = getattr(StripeEvent, "__doc__") # account = ForeignKey(Account, related_name="events") customer = ForeignKey( "Customer", null=True, on_delete=models.CASCADE, help_text= "In the event that there is a related customer, this will point to that Customer record" ) valid = NullBooleanField( null=True, help_text= "Tri-state bool. Null == validity not yet confirmed. Otherwise, this field indicates that this " "event was checked via stripe api and found to be either authentic (valid=True) or in-authentic (possibly " "malicious)") processed = BooleanField( default=False, help_text= "If validity is performed, webhook event processor(s) may run to take further action on the event. " "Once these have run, this is set to True.") @property def message(self): """ The event's data if the event is valid, None otherwise.""" return self.webhook_message if self.valid else None def validate(self): """ The original contents of the Event message comes from a POST to the webhook endpoint. This data must be confirmed by re-fetching it and comparing the fetched data with the original data. That's what this function does. This function makes an API call to Stripe to re-download the Event data. It then marks this record's valid flag to True or False. """ self.valid = self.webhook_message == self.api_retrieve()["data"] self.save() def process(self, force=False, raise_exception=False): """ Invokes any webhook handlers that have been registered for this event based on event type or event sub-type. See event handlers registered in the ``djstripe.event_handlers`` module (or handlers registered in djstripe plugins or contrib packages). :param force: If True, force the event to be processed by webhook handlers, even if the event has already been processed previously. :type force: bool :param raise_exception: If True, any Stripe errors raised during processing will be raised to the caller after logging the exception. :type raise_exception: bool :returns: True if the webhook was processed successfully or was previously processed successfully. :rtype: bool """ if not self.valid: return False if not self.processed or force: exc_value = None try: # TODO: would it make sense to wrap the next 4 lines in a transaction.atomic context? Yes it would, # except that some webhook handlers can have side effects outside of our local database, meaning that # even if we rollback on our database, some updates may have been sent to Stripe, etc in resposne to # webhooks... webhooks.call_handlers(self, self.message, self.event_type, self.event_subtype) self._send_signal() self.processed = True except StripeError as exc: # TODO: What if we caught all exceptions or a broader range of exceptions here? How about DoesNotExist # exceptions, for instance? or how about TypeErrors, KeyErrors, ValueErrors, etc? exc_value = exc self.processed = False EventProcessingException.log(data=exc.http_body, exception=exc, event=self) webhook_processing_error.send(sender=Event, data=exc.http_body, exception=exc) # Saving here now because a previously processed webhook may no # longer be processsed successfully if a re-process was forced but # an event handle was broken. self.save() if exc_value and raise_exception: six.reraise(StripeError, exc_value) return self.processed def _send_signal(self): signal = WEBHOOK_SIGNALS.get(self.type) if signal: return signal.send(sender=Event, event=self) @cached_property def parts(self): """ Gets the event type/subtype as a list of parts. """ return str(self.type).split(".") @cached_property def event_type(self): """ Gets the event type string. """ return self.parts[0] @cached_property def event_subtype(self): """ Gets the event subtype string. """ return ".".join(self.parts[1:])
class Subscription(StripeSubscription): __doc__ = getattr(StripeSubscription, "__doc__") account = ForeignKey( "Account", related_name="subscriptions", help_text="The account associated with this subscription.", null=True, on_delete=models.CASCADE) customer = ForeignKey( "Customer", on_delete=models.CASCADE, related_name="subscriptions", help_text="The customer associated with this subscription.") plan = ForeignKey("Plan", on_delete=models.CASCADE, related_name="subscriptions", help_text="The plan associated with this subscription.") objects = SubscriptionManager() def is_period_current(self): """ Returns True if this subscription's period is current, false otherwise.""" return self.current_period_end > timezone.now() or ( self.trial_end and self.trial_end > timezone.now()) def is_status_current(self): """ Returns True if this subscription's status is current (active or trialing), false otherwise.""" return self.status in ["trialing", "active"] def is_status_temporarily_current(self): """ A status is temporarily current when the subscription is canceled with the ``at_period_end`` flag. The subscription is still active, but is technically canceled and we're just waiting for it to run out. You could use this method to give customers limited service after they've canceled. For example, a video on demand service could only allow customers to download their libraries and do nothing else when their subscription is temporarily current. """ return (self.canceled_at and self.start < self.canceled_at and self.cancel_at_period_end) def is_valid(self): """ Returns True if this subscription's status and period are current, false otherwise. """ if not self.is_status_current(): return False if not self.is_period_current(): return False return True def update(self, prorate=djstripe_settings.PRORATION_POLICY, **kwargs): # Convert Plan to stripe_id if "plan" in kwargs and isinstance(kwargs["plan"], Plan): kwargs.update({"plan": kwargs["plan"].stripe_id}) # Convert Account to stripe_id if "account" in kwargs and isinstance(kwargs["account"], Account): kwargs.update({"stripe_account": kwargs["account"].stripe_id}) stripe_subscription = super(Subscription, self).update(prorate=prorate, **kwargs) return Subscription.sync_from_stripe_data(stripe_subscription) def extend(self, delta): stripe_subscription = super(Subscription, self).extend(delta) return Subscription.sync_from_stripe_data(stripe_subscription) def cancel(self, at_period_end=djstripe_settings.CANCELLATION_AT_PERIOD_END, account=None): # If plan has trial days and customer cancels before trial period ends, # then end subscription now, i.e. at_period_end=False if self.trial_end and self.trial_end > timezone.now(): at_period_end = False stripe_subscription = super(Subscription, self).cancel(at_period_end=at_period_end, account=account) return self def _attach_objects_hook(self, cls, data): self.customer = cls._stripe_object_to_customer(target_cls=Customer, data=data) self.plan = cls._stripe_object_to_plan(target_cls=Plan, data=data) if self.account: self.account = cls._stripe_object_to_account(target_cls=Account, data=data)
class FileDownload(models.Model): ctime = DateTimeField(default=datetime.now()) user = ForeignKey(User) source = ForeignKey(Source) annotated = BooleanField()
class AssignmentGradeHistory(models.Model): user = ForeignKey(User, related_name="u_grade_h") grader = ForeignKey(User, related_name="g_grade_h") ctime = DateTimeField(default=datetime.now()) grade = IntegerField() source = ForeignKey(Source)
class ExtendedUser(AbstractBaseUser): class Meta: index_text = "Manage" verbose_name = 'User' verbose_name_plural = 'Users' first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) username = models.CharField(max_length=50, unique=True) school = ForeignKey(School, blank=True, null=True) STATUS_CHOICES = ( (0, "Personal"), (1, "Director"), (2, "Inspector"), (3, "Admin"), ) status = models.IntegerField(choices=STATUS_CHOICES, verbose_name="user status", default=0) subjects = models.ManyToManyField(Subject, blank=True, null=True) is_active = models.BooleanField(default=True) is_admin = models.BooleanField(default=(status == 3)) objects = ExtendedUserManager() USERNAME_FIELD = 'username' REQUIRED_FIELDS = ['first_name', 'last_name'] perms = { 0: [ "post.view_post", "post.change_own_post", "post.add_own_post", "post.delete_post", "page.change_category", "page.add_category", "page.delete_category", "page.change_subcategory", "page.add_subcategory", "page.delete_subcategory", "page.change_article", "page.add_article", "page.delete_article", "page.change_simplepage", "page.add_simplepage", "page.delete_simplepage" ], 1: [ "school.change_own_school", "post.view_post", "post.change_own_post", "post.add_own_post", "post.delete_post" ], 2: [ "post.view_post", "post.change_own_post", "post.add_own_post", "event.change_own_event", "event.add_own_event", "news.change_own_news", "news.add_own_news", "subject.change_own_subjectpost", "subject.add_own_subjectpost", "subject.change_own_subcategory", "subject.add_own_subcategory", "gallery.change_own_gallery", "gallery.add_own_gallery", "post.delete_post", "event.delete_event", "news.delete_news", "subject.delete_subjectpost", "subject.delete_subcategory", "gallery.delete_gallery" ], 3: ["all"], } def get_full_name(self): return self.first_name + " " + self.last_name def get_short_name(self): return self.first_name def __unicode__(self): return self.first_name + " " + self.last_name def has_perm(self, perm, obj=None): if perm == "frontend.view_module": return True if perm == "frontend.change_module": return False if "all" in self.perms[self.status] or perm in self.perms[self.status]: return True return False def has_module_perms(self, app_label): return True @property def is_staff(self): return True
def __init__(self, to, to_field=None, rel_class=ManyToOneRel, **kwargs): ForeignKey.__init__(self, to, to_field=to_field, rel_class=rel_class, **kwargs) self.on_delete = DO_NOTHING
class Profile(models.Model): user = ForeignKey(User, on_delete=models.CASCADE) plan = ForeignKey(Plan, on_delete=models.CASCADE) def __str__(self): return self.user.username
def __init__(self, to='fias.AddrObj', **kwargs): kwargs.setdefault('related_name', '+') ForeignKey.__init__(self, to, **kwargs)
class Subscriptions(BaseModel): subject_id = ForeignKey(Subject, on_delete=models.RESTRICT) student_id = ForeignKey(Student, on_delete=models.RESTRICT) def __str__(self) -> str: return f'{self.student_id} - {self.subject_id}'
class SettingLabel(models.Model): setting = ForeignKey(DefaultSetting) value = IntegerField() label = TextField()
class Invoice(StripeInvoice): __doc__ = getattr(StripeInvoice, "__doc__") # account = ForeignKey("Account", related_name="invoices") customer = ForeignKey( Customer, on_delete=models.CASCADE, related_name="invoices", help_text="The customer associated with this invoice.") charge = OneToOneField( Charge, null=True, on_delete=models.CASCADE, related_name="invoice", help_text="The latest charge generated for this invoice, if any.") subscription = ForeignKey( "Subscription", null=True, related_name="invoices", on_delete=SET_NULL, help_text="The subscription that this invoice was prepared for, if any." ) class Meta(object): ordering = ["-date"] def get_stripe_dashboard_url(self): return self.customer.get_stripe_dashboard_url() def _attach_objects_hook(self, cls, data): self.customer = cls._stripe_object_to_customer(target_cls=Customer, data=data) charge = cls._stripe_object_to_charge(target_cls=Charge, data=data) if charge: self.charge = charge subscription = cls._stripe_object_to_subscription( target_cls=Subscription, data=data) if subscription: self.subscription = subscription def _attach_objects_post_save_hook(self, cls, data): # InvoiceItems need a saved invoice because they're associated via a # RelatedManager, so this must be done as part of the post save hook. cls._stripe_object_to_invoice_items(target_cls=InvoiceItem, data=data, invoice=self) @classmethod def upcoming(cls, **kwargs): # Convert Customer to stripe_id if "customer" in kwargs and isinstance(kwargs["customer"], Customer): kwargs.update({"customer": kwargs["customer"].stripe_id}) # Convert Subscription to stripe_id if "subscription" in kwargs and isinstance(kwargs["subscription"], Subscription): kwargs.update({"subscription": kwargs["subscription"].stripe_id}) # Convert Plan to stripe_id if "subscription_plan" in kwargs and isinstance( kwargs["subscription_plan"], Plan): kwargs.update( {"subscription_plan": kwargs["subscription_plan"].stripe_id}) upcoming_stripe_invoice = StripeInvoice.upcoming(**kwargs) if upcoming_stripe_invoice: return UpcomingInvoice._create_from_stripe_object( upcoming_stripe_invoice, save=False) @property def plan(self): """ Gets the associated plan for this invoice. In order to provide a consistent view of invoices, the plan object should be taken from the first invoice item that has one, rather than using the plan associated with the subscription. Subscriptions (and their associated plan) are updated by the customer and represent what is current, but invoice items are immutable within the invoice and stay static/unchanged. In other words, a plan retrieved from an invoice item will represent the plan as it was at the time an invoice was issued. The plan retrieved from the subscription will be the currently active plan. :returns: The associated plan for the invoice. :rtype: ``djstripe.Plan`` """ for invoiceitem in self.invoiceitems.all(): if invoiceitem.plan: return invoiceitem.plan if self.subscription: return self.subscription.plan
class UserSetting(models.Model): user = ForeignKey(User) setting = ForeignKey(DefaultSetting) value = IntegerField() ctime = DateTimeField(default=datetime.now)