def test_render(self): # Create test user/group data with changes/deletes. with impersonate(self.superuser): self.create_test_users_and_groups() user = self.users[0] user.first_name = "Firsty" user.last_name = "The Userman" user.save() user = self.users[1] user.delete() group = self.groups[0] group.name = "Grouper Gropers" group.save() group = self.groups[1] group.delete() site = Site.objects.get_current() site.name = "example.org" site.domain = "example.org" site.save() # Verify that each trail is rendered with the appropriate template. for trail in Trail.objects.all(): template_key = (trail.content_type.model_class(), trail.action) html_template = { (Group, "add"): "trails/auth/group/add.html", (Group, "change"): "trails/auth/group/default.html", (Group, "delete"): "trails/auth/group/default.html", (User, "add"): "trails/auth/default.html", (User, "change"): "trails/auth/user/change.html", (User, "delete"): "trails/auth/delete.html", }.get(template_key, "trails/default.html") txt_template = {(Site, "change"): "trails/sites/change.txt"}.get(template_key, "trails/_default.txt") with AssertTemplateUsedContext(self, html_template): trail.render("html") with AssertTemplateUsedContext(self, txt_template): trail.render("txt")
def test_middleware(self): self.create_test_users_and_groups() self.assertEqual(get_current_user(), None) url = reverse("test_app:index") # Test anonymous user. response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, "AnonymousUser") self.assertEqual(get_current_user(), None) # Test logged in user. self.client.login(username=self.users[0].username, password=self.user_passwords[0]) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, unicode(self.users[0])) self.assertEqual(get_current_user(), None) # Test impersonate context manager. with impersonate(self.users[0]): self.assertEqual(get_current_user(), self.users[0]) self.assertEqual(get_current_user(), None) # Test impersonate(None) within view requested by logged in user. self.client.login(username=self.users[0].username, password=self.user_passwords[0]) response = self.client.get(url + "?impersonate=1") self.assertEqual(response.status_code, 200) self.assertEqual(response.content, unicode(None)) self.assertEqual(get_current_user(), None) # Test when request raises exception. try: response = self.client.get(url + "?raise=1") except RuntimeError: response = None self.assertEqual(response, None) self.assertEqual(get_current_user(), None)
def get(self, request, format=None): if request.query_params.get('raise', ''): raise RuntimeError() if request.query_params.get('impersonate', ''): with impersonate(None): current_user = six.text_type(get_current_user()) else: current_user = six.text_type(get_current_user()) return Response(current_user)
def index(request): if request.GET.get('raise', ''): raise RuntimeError() if request.GET.get('impersonate', ''): with impersonate(None): current_user = smart_text(get_current_user()) else: current_user = smart_text(get_current_user()) return HttpResponse(current_user, content_type='text/plain')
def test_admin(self): app_label = Trail._meta.app_label model_name = getattr(Trail._meta, "model_name", None) or getattr(Trail._meta, "module_name", None) self.assertTrue(model_name) self.client.login(username=self.superuser.username, password=self.superuser_password) with impersonate(self.superuser): self.create_test_users_and_groups() with impersonate(self.users[0]): self.create_test_users_and_groups(prefix="extra") with impersonate(self.superuser): self.users[0].delete() self.users.pop(0) self.user_passwords.pop(0) self.assertTrue(Trail.objects.count()) # Change list view. changelist_url = reverse("admin:%s_%s_changelist" % (app_label, model_name)) response = self.client.get(changelist_url) self.assertEquals(response.status_code, 200) for trail in Trail.objects.all(): change_url = reverse("admin:%s_%s_change" % (app_label, model_name), args=(trail.pk,)) change_url_found = False soup = BeautifulSoup(response.content) for atag in soup.findAll("a", href=True): target_url = urlparse.urljoin(changelist_url, atag["href"]) if target_url == change_url: change_url_found = True break self.assertTrue(change_url_found) # Change view (should have no editable fields). response = self.client.get(change_url) self.assertEquals(response.status_code, 200) soup = BeautifulSoup(response.content) for fs in soup.findAll("fieldset"): self.assertFalse(fs.findAll("input")) self.assertFalse(fs.findAll("select")) self.assertFalse(fs.findAll("textarea")) # Delete view via GET, then POST. delete_url = reverse("admin:%s_%s_delete" % (app_label, model_name), args=(trail.pk,)) response = self.client.get(delete_url) self.assertEquals(response.status_code, 200) # Add view should raise a 403. add_url = reverse("admin:%s_%s_add" % (app_label, model_name)) response = self.client.get(add_url) self.assertEquals(response.status_code, 403)
def test_manager_for_models(self): with impersonate(self.superuser): self.create_test_users_and_groups() # All normal users. instances = self.users trails = Trail.objects.for_models(*instances) self.assertTrue(trails.count()) for trail in trails: self.assertTrue(trail.content_object in instances) # All normal groups. instances = self.groups trails = Trail.objects.for_models(*instances) self.assertTrue(trails.count()) for trail in trails: self.assertTrue(trail.content_object in instances) # Mix of user and group. instances = [self.users[0], self.groups[0]] trails = Trail.objects.for_models(*instances) self.assertTrue(trails.count()) for trail in trails: self.assertTrue(trail.content_object in instances) # Multiples of one type, mixed with another type. instances = self.users + [self.groups[0]] trails = Trail.objects.for_models(*instances) self.assertTrue(trails.count()) for trail in trails: self.assertTrue(trail.content_object in instances) # Passing None as an instance should work, but return no trails. instances = [None] trails = Trail.objects.for_models(*instances) self.assertFalse(trails.count()) # Passing a single Queryset instead of a list. instances = Group.objects.all() trails = Trail.objects.for_models(*instances) self.assertTrue(trails.count()) for trail in trails: self.assertTrue(trail.content_object in instances) # Passing a list of Querysets. instances = [Group.objects.all(), User.objects.all()] trails = Trail.objects.for_models(*instances) self.assertTrue(trails.count()) for trail in trails: self.assertTrue(trail.content_object in instances[0] or trail.content_object in instances[1])
def test_middleware(self): # For test coverage. import crum imp.reload(crum) # Test anonymous user. self.assertEqual(get_current_user(), None) url = reverse('test_app:index') response = self.client.get(url) response_content = response.content.decode('utf-8') self.assertEqual(response.status_code, 200) self.assertEqual(response_content, 'AnonymousUser') self.assertEqual(get_current_user(), None) # Test logged in user. self.client.login(username=self.user.username, password=self.user_password) response = self.client.get(url) response_content = response.content.decode('utf-8') self.assertEqual(response.status_code, 200) self.assertEqual(response_content, six.text_type(self.user)) self.assertEqual(get_current_user(), None) # Test impersonate context manager. with impersonate(self.user): self.assertEqual(get_current_user(), self.user) self.assertEqual(get_current_user(), None) # Test impersonate(None) within view requested by logged in user. self.client.login(username=self.user.username, password=self.user_password) response = self.client.get(url + '?impersonate=1') response_content = response.content.decode('utf-8') self.assertEqual(response.status_code, 200) self.assertEqual(response_content, six.text_type(None)) self.assertEqual(get_current_user(), None) # Test when request raises exception. try: response = self.client.get(url + '?raise=1') except RuntimeError: response = None self.assertEqual(response, None) self.assertEqual(get_current_user(), None)
def test_job_relaunch_permission_denied_response_other_user( get, post, inventory, project, alice, bob, survey_spec_factory): ''' Asserts custom permission denied message corresponding to awx/main/tests/functional/test_rbac_job.py::TestJobRelaunchAccess::test_other_user_prompts ''' jt = JobTemplate.objects.create(name='testjt', inventory=inventory, project=project, ask_credential_on_launch=True, ask_variables_on_launch=True, survey_spec=survey_spec_factory([{ 'variable': 'secret_key', 'default': '6kQngg3h8lgiSTvIEb21', 'type': 'password' }]), survey_enabled=True) jt.execute_role.members.add(alice, bob) with impersonate(bob): job = jt.create_unified_job(extra_vars={ 'job_var': 'foo2', 'secret_key': 'sk4t3Rb01' }) # User capability is shown for this r = get(job.get_absolute_url(), alice, expect=200) assert r.data['summary_fields']['user_capabilities']['start'] # Job has prompted data, launch denied w/ message r = post(url=reverse('api:job_relaunch', kwargs={'pk': job.pk}), data={}, user=alice, expect=403) assert 'Job was launched with secret prompts provided by another user' in r.data[ 'detail']
def test_activity_stream_deleted_actor(alice, bob): alice.first_name = 'Alice' alice.last_name = 'Doe' alice.save() with impersonate(alice): o = Organization.objects.create(name='test organization') entry = o.activitystream_set.get(operation='create') assert entry.actor == alice alice.delete() entry = o.activitystream_set.get(operation='create') assert entry.actor is None deleted = entry.deleted_actor assert deleted['username'] == 'alice' assert deleted['first_name'] == 'Alice' assert deleted['last_name'] == 'Doe' entry.actor = bob entry.save(update_fields=['actor']) deleted = entry.deleted_actor entry = ActivityStream.objects.get(id=entry.pk) assert entry.deleted_actor['username'] == 'bob'
def test_job_relaunch_permission_denied_response(post, get, inventory, project, credential, net_credential, machine_credential): jt = JobTemplate.objects.create(name='testjt', inventory=inventory, project=project) jt.credentials.add(machine_credential) jt_user = User.objects.create(username='******') jt.execute_role.members.add(jt_user) with impersonate(jt_user): job = jt.create_unified_job() # User capability is shown for this r = get(job.get_absolute_url(), jt_user, expect=200) assert r.data['summary_fields']['user_capabilities']['start'] # Job has prompted extra_credential, launch denied w/ message job.launch_config.credentials.add(net_credential) r = post(reverse('api:job_relaunch', kwargs={'pk': job.pk}), {}, jt_user, expect=403) assert 'launched with prompted fields' in r.data['detail'] assert 'do not have permission' in r.data['detail']
def test_update_old_mitigated_with_custom_edit(self, mock_can_edit, mock_tz): mock_tz.return_value = frozen_datetime custom_mitigated = datetime.datetime.now() with impersonate(self.user_1): test = Test.objects.last() finding = Finding(test=test, is_mitigated=True, active=False, mitigated=frozen_datetime, mitigated_by=self.user_1) finding.save() finding.is_mitigated = True finding.active = False finding.mitigated = custom_mitigated finding.mitigated_by = self.user_2 finding.save() self.assertEqual(self.get_status_fields(finding), (False, True, False, False, True, custom_mitigated, self.user_2, frozen_datetime))
def test_audit_log_read_actor_role(user, role, caplog, staff_user, anon_user): youth_profile = YouthProfileFactory() if role == "SYSTEM": user = None elif role == "OWNER": user = youth_profile.user elif role == "ADMIN": user = staff_user elif role == "ANONYMOUS": user = anon_user caplog.clear() with impersonate(user): YouthProfile.objects.first() logs = get_log_records(caplog) assert len(logs) == 1 log_message = json.loads(logs[0]) assert log_message["audit_event"]["actor"]["role"] == role if user: assert log_message["audit_event"]["actor"]["user_id"] == (str( user.uuid) if user != anon_user else None) assert log_message["audit_event"]["actor"][ "user_name"] == user.username
def test_update_old_mitigated_with_missing_data(self, mock_can_edit, mock_tz): mock_tz.return_value = frozen_datetime custom_mitigated = datetime.datetime.now() with impersonate(self.user_1): test = Test.objects.last() finding = Finding(test=test, is_mitigated=True, active=False, mitigated=custom_mitigated, mitigated_by=self.user_2) finding.save() finding.is_mitigated = True finding.active = False # trying to remove mitigated fields will trigger the signal to set them to now/current user finding.mitigated = None finding.mitigated_by = None finding.save() self.assertEqual(self.get_status_fields(finding), (False, True, False, False, True, frozen_datetime, self.user_1, frozen_datetime))
def delete_inventory(inventory_id, user_id, retries=5): # Delete inventory as user if user_id is None: user = None else: try: user = User.objects.get(id=user_id) except Exception: user = None with ignore_inventory_computed_fields(), ignore_inventory_group_removal( ), impersonate(user): try: i = Inventory.objects.get(id=inventory_id) for host in i.hosts.iterator(): host.job_events_as_primary_host.update(host=None) i.delete() emit_channel_notification( 'inventories-status_changed', { 'group_name': 'inventories', 'inventory_id': inventory_id, 'status': 'deleted' }) logger.debug('Deleted inventory {} as user {}.'.format( inventory_id, user_id)) except Inventory.DoesNotExist: logger.exception( "Delete Inventory failed due to missing inventory: " + str(inventory_id)) return except DatabaseError: logger.exception( 'Database error deleting inventory {}, but will retry.'.format( inventory_id)) if retries > 0: time.sleep(10) delete_inventory(inventory_id, user_id, retries=retries - 1)
def handle(self, *args, **kwargs): # Sanity check: Is there already an organization in the system? if Setting.objects.count(): print('System is already configured, exiting.') print('(changed: False)') return User = get_user_model() User.objects.create_superuser('*****@*****.**', 'adminadmin') call_command("loaddata", "settings") # Create a default organization as the first superuser found. try: superuser = User.objects.filter( is_superuser=True).order_by('pk')[0] except IndexError: superuser = None with impersonate(superuser): r = Repository.objects.create(name='Demo Repository', path='/tmp/repository', repository_key='0123456789abcdef', enabled=False) s = Schedule.objects.create(name='Demo Schedule', crontab='0 5 * * MON *', enabled=False) c = Client.objects.create(hostname='localhost', enabled=False) p = Policy(name='Demo Policy', mode_pull=False, enabled=False, repository=r, schedule=s) p.save() p.clients.add(c) print('Demo Client, Repository, Schedule and Policy added.') print('(changed: True)')
def create_engagement_epic(self, engagement): with impersonate(self.testuser): return jira_helper.add_epic(engagement)
def handle(self, *args, **kwargs): changed = False # Create a default organization as the first superuser found. try: superuser = User.objects.filter( is_superuser=True).order_by('pk')[0] except IndexError: superuser = None with impersonate(superuser): with disable_computed_fields(): if not Organization.objects.exists(): o, _ = Organization.objects.get_or_create(name='Default') # Avoid calling directly the get_or_create() to bypass project update p = Project.objects.filter(name='Demo Project', scm_type='git').first() if not p: p = Project( name='Demo Project', scm_type='git', scm_url= 'https://github.com/ansible/ansible-tower-samples', scm_update_cache_timeout=0, status='successful', scm_revision= '347e44fea036c94d5f60e544de006453ee5c71ad', playbook_files=['hello_world.yml'], ) p.organization = o p.save(skip_update=True) ssh_type = CredentialType.objects.filter( namespace='ssh').first() c, _ = Credential.objects.get_or_create( credential_type=ssh_type, name='Demo Credential', inputs={'username': superuser.username}, created_by=superuser) c.admin_role.members.add(superuser) public_galaxy_credential, _ = Credential.objects.get_or_create( name='Ansible Galaxy', managed=True, credential_type=CredentialType.objects.get( kind='galaxy'), inputs={'url': 'https://galaxy.ansible.com/'}, ) o.galaxy_credentials.add(public_galaxy_credential) i, _ = Inventory.objects.get_or_create( name='Demo Inventory', organization=o, created_by=superuser) Host.objects.get_or_create( name='localhost', inventory=i, variables= "ansible_connection: local\nansible_python_interpreter: '{{ ansible_playbook_python }}'", created_by=superuser, ) jt = JobTemplate.objects.filter( name='Demo Job Template').first() if jt: jt.project = p jt.inventory = i jt.playbook = 'hello_world.yml' jt.save() else: jt, _ = JobTemplate.objects.get_or_create( name='Demo Job Template', playbook='hello_world.yml', project=p, inventory=i) jt.credentials.add(c) print('Default organization added.') print( 'Demo Credential, Inventory, and Job Template added.') changed = True if changed: print('(changed: True)') else: print('(changed: False)')
def handle(self, *args, **kwargs): changed = False # Create a default organization as the first superuser found. try: superuser = User.objects.filter( is_superuser=True).order_by('pk')[0] except IndexError: superuser = None with impersonate(superuser): with disable_computed_fields(): if not Organization.objects.exists(): o = Organization.objects.create(name='Default') p = Project( name='Demo Project', scm_type='git', scm_url= 'https://github.com/ansible/ansible-tower-samples', scm_update_on_launch=True, scm_update_cache_timeout=0, organization=o) p.save(skip_update=True) ssh_type = CredentialType.objects.filter( namespace='ssh').first() c = Credential.objects.create( credential_type=ssh_type, name='Demo Credential', inputs={'username': superuser.username}, created_by=superuser) c.admin_role.members.add(superuser) public_galaxy_credential = Credential( name='Ansible Galaxy', managed_by_tower=True, credential_type=CredentialType.objects.get( kind='galaxy'), inputs={'url': 'https://galaxy.ansible.com/'}) public_galaxy_credential.save() o.galaxy_credentials.add(public_galaxy_credential) i = Inventory.objects.create(name='Demo Inventory', organization=o, created_by=superuser) Host.objects.create( name='localhost', inventory=i, variables= "ansible_connection: local\nansible_python_interpreter: '{{ ansible_playbook_python }}'", created_by=superuser) jt = JobTemplate.objects.create(name='Demo Job Template', playbook='hello_world.yml', project=p, inventory=i) jt.credentials.add(c) print('Default organization added.') print( 'Demo Credential, Inventory, and Job Template added.') changed = True default_ee = settings.AWX_EXECUTION_ENVIRONMENT_DEFAULT_IMAGE ee, created = ExecutionEnvironment.objects.get_or_create( name='Default EE', defaults={ 'image': default_ee, 'managed_by_tower': True }) if created: changed = True print('Default Execution Environment registered.') if changed: print('(changed: True)') else: print('(changed: False)')
def handle(self, *args, **options): # use admin user to make sure we have access to its properties i.e. to determine wants_async with impersonate(Dojo_User.objects.get(username='******')): ra_helper.expiration_handler()
def test_activity_stream_actor(admin_user): with impersonate(admin_user): o = Organization.objects.create(name='test organization') entry = o.activitystream_set.get(operation='create') assert entry.actor == admin_user
def can_receive_discount(user, course, discount_expiration_date=None): """ Check all the business logic about whether this combination of user and course can receive a discount. """ # Always disable discounts until we are ready to enable this feature with impersonate(user): if not DISCOUNT_APPLICABILITY_FLAG.is_enabled(): return False # TODO: Add additional conditions to return False here # Check if discount has expired if not discount_expiration_date: discount_expiration_date = get_discount_expiration_date(user, course) if discount_expiration_date is None: return False if discount_expiration_date < timezone.now(): return False # Course end date needs to be in the future if course.has_ended(): return False # Course needs to have a non-expired verified mode modes_dict = CourseMode.modes_for_course_dict(course=course, include_expired=False) verified_mode = modes_dict.get('verified', None) if not verified_mode: return False # Site, Partner, Course or Course Run not excluded from lms-controlled discounts if DiscountRestrictionConfig.disabled_for_course_stacked_config(course): return False if user.is_anonymous: return False # Don't allow users who have enrolled in any courses in non-upsellable # modes if CourseEnrollment.objects.filter(user=user).exclude( mode__in=CourseMode.UPSELL_TO_VERIFIED_MODES).exists(): return False # Don't allow any users who have entitlements (past or present) if CourseEntitlement.objects.filter(user=user).exists(): return False # We can't import this at Django load time within the openedx tests settings context from openedx.features.enterprise_support.utils import is_enterprise_learner # Don't give discount to enterprise users if is_enterprise_learner(user): return False # Excute holdback if _is_in_holdback(user): return False return True
def handle(self, *args, **options): logger.info("Bootstrapping database with mock data...") # add Users (which then adds actors) self.mock(model=User, factory=UserFactory, count=100, bulk=False) # add admin admin = User.objects.create(username="******", is_staff=True, is_superuser=True) admin.set_password("admin") admin.save() # create all the scaffolding as the admin user with impersonate(admin): #################### # add teams carteam = Team.objects.create( name="Car Nerds", description="A group of people who drive and review cars", founder=Actor.objects.all().order_by("?").first(), ) foodteam = Team.objects.create( name="Foodies", description= "Hungry people with opinions on food and places that serve food", founder=Actor.objects.all().order_by("?").first(), ) Team.objects.create( name="Music Lovers", description= "Concert-going music lovers. Reviews of venues and concerts.", founder=Actor.objects.all().order_by("?").first(), ) ################### # add team members for all teams for team in Team.objects.all(): # add site admin as admin team member of all teams Membership.objects.create(team=team, actor=admin.actor, admin=True) # add all remaining Actors with no team to a random team for actor in Actor.objects.filter(memberships__isnull=True): Membership.objects.create( team=Team.objects.all().order_by("?").first(), actor=actor) # add categories for carteam makecat = Category.objects.create(team=carteam, name="Car Makers", description="A car maker.") carcat = Category.objects.create(team=carteam, name="Cars", description="A car.") # add Contexts for carteam classic_car_context = Context.objects.create( team=carteam, name="Classic Car Meetup, Carville, 2018", description= "Use this context for all reviews from the 2018 meetup in Carville.", ) japanese_car_context = Context.objects.create( team=carteam, name="Jap Car Fest 2018", description= 'Use this context for all reviews from the 2018 "Jap Car Fest"', ) # add attributes for Maker category Fact.objects.create( name="Country", datatype=Fact.TYPE_TEXT, category=makecat, required=True, slug=makecat.create_fact_slug("Country"), description="The country this car maker is from", ) # add django-eav2 Facts for the "Car" Category Fact.objects.create( name="Make", datatype=Fact.TYPE_OBJECT, category=carcat, required=True, slug=carcat.create_fact_slug("Make"), description="The make of this car", object_category=makecat, ) Fact.objects.create( name="Colour", datatype=Fact.TYPE_TEXT, category=carcat, required=True, slug=carcat.create_fact_slug("Colour"), description="The colour of this car", ) Fact.objects.create( name="BHP", datatype=Fact.TYPE_INT, category=carcat, required=True, slug=carcat.create_fact_slug("BHP"), description="The BHP of this car", ) # add Ratings for "Car" category Rating.objects.create( name="Looks", category=carcat, max_rating=10, description="Rate the looks of this car, 10 is best.", ) Rating.objects.create( name="Handling", category=carcat, max_rating=10, description="How well the car handles. 10 is best.", ) # add car makers ford = makecat.items.create(name="Ford", eav__country="US") bmw = makecat.items.create(name="BMW", eav__country="DE") peugeot = makecat.items.create(name="Peugeot", eav__country="FR") # add cars carcat.items.create( name="Escort RS Cosworth", eav__make=ford, eav__colour="black", eav__bhp="170", ) carcat.items.create(name="Focus", eav__make=ford, eav__colour="blue", eav__bhp="95") carcat.items.create(name="520i E34", eav__make=bmw, eav__colour="purple", eav__bhp="200") carcat.items.create(name="M3", eav__make=bmw, eav__colour="white", eav__bhp="280") carcat.items.create( name="406 Estate", eav__make=peugeot, eav__colour="white", eav__bhp="130", ) # add reviews for cars for car in carcat.items.all(): # not everyone votes :( votercount = random.randint(1, carcat.team.members.count()) for actor in carcat.team.members.all()[0:votercount]: with impersonate(actor.user): review = Review.objects.create( actor=actor, item=car, headline=factory.Faker("sentence").generate(), body=factory.Faker("text").generate() if random.choice([True, False]) else None, context=random.choice( [classic_car_context, japanese_car_context]), ) logger.debug("created review %s" % review) # add votes to this review for rating in carcat.ratings.all(): # 50/50 chance if this actor voted for this Rating if random.choice([True, False]): # 50/50 chance if this actor left a comment for this Vote if random.choice([True, False]): comment = factory.Faker("sentence").generate() else: comment = None # create the Vote vote = Vote.objects.create( review=review, rating=rating, vote=random.randint(1, rating.max_rating), comment=comment, ) logger.debug("created vote %s" % vote) # add categories for foodteam restaurant = Category.objects.create( team=foodteam, name="Restaurant", description="A place where food is served") dish = Category.objects.create(team=foodteam, name="Dish", description="Some food") # add attributes for "Restaurant" category Fact.objects.create( name="Location", datatype=Fact.TYPE_POINT, required=True, category=restaurant, slug=restaurant.create_fact_slug("Location"), description="The location (coordinates) of this restaurant", ) Fact.objects.create( name="Description", datatype=Fact.TYPE_TEXT, required=True, category=restaurant, slug=restaurant.create_fact_slug("Description"), description= "A short description of this restaurant. Markdown should be supported.", ) # add attributes for "Dish" category Fact.objects.create( name="Price", datatype=Fact.TYPE_INT, required=True, category=dish, slug=restaurant.create_fact_slug("Price"), description="The price in EUR of this dish", ) Fact.objects.create( name="Date Eaten", datatype=Fact.TYPE_DATE, required=True, category=dish, slug=restaurant.create_fact_slug("Date Eaten"), description="The date and time this Dish was eaten", ) # TODO: add restaurants and dishes logger.info( "Done. A superuser was added, username admin password admin. Django admin access and member of all teams. Enjoy!" )