-
-
Notifications
You must be signed in to change notification settings - Fork 800
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Impossible to swap models #634
Comments
I think this comment: #605 (comment) offers a potential solution via using the |
Hey @phillbaker, no
You basically need to manually amend the auto-generated migration This is an extract of what I ended up with operations = [
migrations.CreateModel(
name='AccessToken',
fields=[
('id', models.BigAutoField(primary_key=True, serialize=False)),
('expires', models.DateTimeField()),
('scope', models.TextField(blank=True)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('token', models.TextField(unique=True)),
('application', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='common_accesstoken', to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
'swappable': 'OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL',
},
),
migrations.CreateModel(
name='RefreshToken',
fields=[
('id', models.BigAutoField(primary_key=True, serialize=False)),
('token', models.CharField(max_length=255)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('access_token', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='refresh_token', to=settings.OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL)),
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='common_refreshtoken', to=settings.AUTH_USER_MODEL)),
('revoked', models.DateTimeField(null=True)),
],
options={
'abstract': False,
'swappable': 'OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL',
},
),
migrations.AlterUniqueTogether(
name='refreshtoken',
unique_together=set([('token', 'revoked')]),
),
migrations.AddField(
model_name='accesstoken',
name='source_refresh_token',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL, related_name='refreshed_access_token'),
preserve_default=False,
)
] |
Use django oauth toolkit to add oauth2 endpoints. The library's swappable models are not very well designed see jazzband/django-oauth-toolkit#634 so I had to battle a little bit to finally manage to define our own custom AccessToken model. The steps that I followed: 1. define a custom app conf for oauth2 provider to ignore their models 2. ignore the oauth2 provider migrations thanks to django's MIGRATION_MODULES setting Our oauth2 tokens are JWT => I had to customize the token generation part (see mds/authent/oauthlib_utils.py) Finally I added a custom endpoint to generate long lived tokens (for providers unable to talk oauth2).
Same issue here. Trying to swap those models in my project via:
raises this error when applying migrations:
|
Have the same problem when trying to swap |
I have the same exact problem. Anyone has been able to find a solution for this yet ? |
This is how I've fixed this. I defined the models as follow: # oauth/models.py
class Application(models.Model):
pass
class Grant(models.Model):
pass
class AccessToken(models.Model):
pass
class RefreshToken(models.Model):
pass Then did # oauth/models.py
class Application(oauth2_models.AbstractApplication):
pass
class Grant(oauth2_models.AbstractGrant):
pass
class AccessToken(oauth2_models.AbstractAccessToken):
pass
class RefreshToken(oauth2_models.AbstractRefreshToken):
pass Set the swapable models to point to these. # settings.py
# OAuth
OAUTH2_PROVIDER_APPLICATION_MODEL = "oauth.Application"
OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = "oauth.AccessToken"
OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "oauth.RefreshToken"
OAUTH2_PROVIDER_GRANT_MODEL = "oauth.Grant" Once again, did |
We can conclude that the only working "out-of-the-box" swappable model is the Application model (which is the only covered by documentation). |
@Alir3z4 I'm trying the above and am still seeing the E.305 reverse accessor errors:
What did I miss? Thanks. |
Has this broken since release 1.1? https://gitmemory.com/issue/jazzband/django-oauth-toolkit/634/471959496 |
See https://docs.djangoproject.com/en/3.0/topics/db/models/#abstract-related-name |
I tried the above steps. Still facing the same problem( Error fields.E305) . Is there any workaround to fix this issue. |
After spinning around a lot with E304's and so on, I got this to work but I don't believe this is truly a swappable set of models (but maybe it is). What I did:
This is really not a swappable model as far as I understand what that means. But it was a way to extend the AccessToken model which I can than override the validator class for:
I'm doing the above because I want to use some locally-added claims from my external OAuth2/OIDC AS introspection endpoint. This is kind of non-standard but my AS lets me configure added response fields. |
@n2ygk Could you elaborate on the |
@armando-herastang sure. Here it is. It's basically the same as the 0001 migration in DOT; I've just added an extra field to
|
@n2ygk . Thanks for the quick response, but I am still getting the same issue. I want to do the same thing you did. I want to add a field that I will populate in a custom
|
Do you have your 'oauth' app in INSTALLED_APPS? Here's my complete:
Make sure you also have this in settings (I'm not sure the grant one is needed):
|
@n2ygk . I do, although I do have this app inside a couple of folders, and I do have it in the
And it's name on
Then, on my
I notice
I'm sorry, but maybe I am missing something. Thanks for the help |
This is a really annoying feature of this stuff that I wasted a lot of time looking at. It looks like a typical string-style module import but if you dig into the code, you'll see it does a simple |
What is the status of this issue? I'm having a really rough time trying to swap out the Is what @faxioman said correct?
If not, is there a set of reproducible steps that allow one to override the access token model? |
@danlamanna I am sorry, but I wasn't able to do it either. |
https://github.com/wq/django-swappable-models Could someone add this alpha stealth django feature to the project? It seems it would allow these circular references to be handled out of the box and in the future all the models would easily be customizable. Looking for some feedback before this gets undertaken so custom access keys are easier to implement for others in the future. |
I did what @Alir3z4 did, the
Please help me. I've been doing this since yesterday and weren't able to sleep properly. I keep on thinking about this. My models:
My settings:
|
@n2ygk Given my personal experience and what some of the others in this issue are saying, it's only safe to conclude that dozens or hundreds of man hours have been wasted trying to configure these models over the last few years. It seems clear that these models aren't swappable in practice. Is there something we can do to prevent this from happening in the future? A warning when trying to configure these settings, a change in documentation, etc? |
This is still in issue, in case anyone thought it went away :D |
Hey guys, I just wanted to let everyone on this thread know that I think I found a hacky workaround using The major issue is django refuses to run the migration swapping the oauth2 models cause they don't exist yet, and the hacks you can do locally to make it work are not practical when releasing to prod. However, you can just lie to django apparently. go into your initial migration (0001_initial.py), and add this to the
It wont actually build the table, but django will think you did, so it wont fail it's pre-migrate checks later. then, generate an empty migration in the same app and copy-paste the faked table create operations over into
this time, it will create the tables, but django wont be aware of it, so you wont get a "table exists" error or anything. Now you should be able to swap the models in your settings, make and run migrations, and it'll work as you originally expected it to. I have no idea what kind of unintended consequences could arise from lying to django this way, so use this workaround at your own risk. |
If you still have problems here is solution that worked for me: Summary:
1. Add to settings.py:
2. Implement models:
3. Run makemigrations and migrate What was most important? You need to implement and overwrite ALL models (Access, Refresh, ID, Application) |
@michaeljaszczuk Can you share detail about how to set up your custom model ?. I am flowing you but not I can't. |
What exactly do you need to know? Have you seen my requirements? Clean db, no migrations ran and packages versions? If so,
That worked for me 🤔 Code is above... Let me know what is unclear and i will try to help! |
I confirm @michaeljaszczuk solution #634 (comment) works as expected. |
Perhaps someone would submit a documentation PR for this? |
I have spent the better part of 2 days just trying to customise the Application model, which is the one model people claim is extendible! Here's what I did. I'd really appreciate any help or explanation. Approach 1: follow the docsStarting with a brand new project, I follow these instructions here: https://django-oauth-toolkit.readthedocs.io/en/latest/advanced_topics.html to the letter
So the conclusion appears to be: I can't swap the model before migrating Approach 2: swap the model after the docs flow
|
I should add: I'm using django-oauth-toolkit v2.0. And I see that in our other project (which has I guess this might not affect you if you are upgrading from v1.x to v2.0 (because migrations 0001-0005 will have been run a long time ago). But it is definitely breaking new v2.0 setups. |
When I try Approach 1 described above using oauth-toolkit v1.7.1, it just works. There goes 2 days of my life 👋 |
Just curious to see if setting up my custom Application in 1.7.1 and then upgrading to 2.0.0 would work -- but migration 0006 also causes an error in this case.
|
@binnev See pinned issue #1146 which is pending a fix. I hope to get to this in the coming week or so. |
📝 what I did to setup custom models, for future reference:
Now you can start customizing models in Following are the end results that you could just copy to your codebase: models.pyfrom django.db import models
from oauth2_provider.models import (
AbstractAccessToken,
AbstractApplication,
AbstractGrant,
AbstractIDToken,
AbstractRefreshToken,
)
class OAuth2AccessToken(AbstractAccessToken):
class Meta(AbstractAccessToken.Meta):
db_table = "oauth2_provider_accesstoken__custom"
class OAuth2RefreshToken(AbstractRefreshToken):
class Meta(AbstractRefreshToken.Meta):
db_table = "oauth2_provider_refreshtoken__custom"
class OAuth2Application(AbstractApplication):
class Meta(AbstractApplication.Meta):
db_table = "oauth2_provider_application__custom"
class OAuth2Grant(AbstractGrant):
class Meta(AbstractGrant.Meta):
db_table = "oauth2_provider_grant__custom"
class OAuth2IdToken(AbstractIDToken):
class Meta(AbstractIDToken.Meta):
db_table = "oauth2_provider_idtoken__custom" migrations/0001_initial.py# Generated by Django 3.2.13 on 2022-06-21 12:48
import uuid
import django.db.models.deletion
import django.utils.timezone
import oauth2_provider.generators
import oauth2_provider.models
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
run_before = [
("oauth2_provider", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="OAuth2AccessToken",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
],
options={
"db_table": "oauth2_provider_accesstoken__custom",
},
),
migrations.CreateModel(
name="OAuth2Application",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
],
options={
"db_table": "oauth2_provider_application__custom",
},
),
migrations.CreateModel(
name="OAuth2Grant",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
],
options={
"db_table": "oauth2_provider_grant__custom",
},
),
migrations.CreateModel(
name="OAuth2RefreshToken",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
],
options={
"db_table": "oauth2_provider_refreshtoken__custom",
},
),
migrations.CreateModel(
name="OAuth2IdToken",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
],
options={
"db_table": "oauth2_provider_idtoken__custom",
},
),
migrations.AddField(
model_name="oauth2accesstoken",
name="application",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL,
),
),
migrations.AddField(
model_name="oauth2accesstoken",
name="created",
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2accesstoken",
name="expires",
field=models.DateTimeField(default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2accesstoken",
name="id_token",
field=models.OneToOneField(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="access_token",
to=settings.OAUTH2_PROVIDER_ID_TOKEN_MODEL,
),
),
migrations.AddField(
model_name="oauth2accesstoken",
name="scope",
field=models.TextField(blank=True),
),
migrations.AddField(
model_name="oauth2accesstoken",
name="source_refresh_token",
field=models.OneToOneField(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="refreshed_access_token",
to=settings.OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL,
),
),
migrations.AddField(
model_name="oauth2accesstoken",
name="token",
field=models.CharField(max_length=255, default="", unique=True),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2accesstoken",
name="updated",
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name="oauth2accesstoken",
name="user",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="sample_identity_oauth2_provider_oauth2accesstoken",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AddField(
model_name="oauth2application",
name="algorithm",
field=models.CharField(
blank=True,
choices=[("", "No OIDC support"), ("RS256", "RSA with SHA-2 256"), ("HS256", "HMAC with SHA-2 256")],
default="",
max_length=5,
),
),
migrations.AddField(
model_name="oauth2application",
name="authorization_grant_type",
field=models.CharField(
choices=[
("authorization-code", "Authorization code"),
("implicit", "Implicit"),
("password", "Resource owner password-based"),
("client-credentials", "Client credentials"),
("openid-hybrid", "OpenID connect hybrid"),
],
default="",
max_length=32,
),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2application",
name="client_id",
field=models.CharField(db_index=True, default=oauth2_provider.generators.generate_client_id, max_length=100, unique=True),
),
migrations.AddField(
model_name="oauth2application",
name="client_secret",
field=oauth2_provider.models.ClientSecretField(
blank=True,
db_index=True,
default=oauth2_provider.generators.generate_client_secret,
help_text="Hashed on Save. Copy it now if this is a new secret.",
max_length=255,
),
),
migrations.AddField(
model_name="oauth2application",
name="client_type",
field=models.CharField(
choices=[("confidential", "Confidential"), ("public", "Public")], default="", max_length=32
),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2application",
name="created",
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2application",
name="name",
field=models.CharField(blank=True, max_length=255),
),
migrations.AddField(
model_name="oauth2application",
name="redirect_uris",
field=models.TextField(blank=True, help_text="Allowed URIs list, space separated"),
),
migrations.AddField(
model_name="oauth2application",
name="skip_authorization",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="oauth2application",
name="updated",
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name="oauth2application",
name="user",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="sample_identity_oauth2_provider_oauth2application",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AddField(
model_name="oauth2grant",
name="application",
field=models.ForeignKey(
default=0, on_delete=django.db.models.deletion.CASCADE, to="sample_identity_oauth2_provider.oauth2application"
),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2grant",
name="claims",
field=models.TextField(blank=True),
),
migrations.AddField(
model_name="oauth2grant",
name="code",
field=models.CharField(default="", max_length=255, unique=True),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2grant",
name="code_challenge",
field=models.CharField(blank=True, default="", max_length=128),
),
migrations.AddField(
model_name="oauth2grant",
name="code_challenge_method",
field=models.CharField(
blank=True, choices=[("plain", "plain"), ("S256", "S256")], default="", max_length=10
),
),
migrations.AddField(
model_name="oauth2grant",
name="created",
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2grant",
name="expires",
field=models.DateTimeField(default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2grant",
name="nonce",
field=models.CharField(blank=True, default="", max_length=255),
),
migrations.AddField(
model_name="oauth2grant",
name="redirect_uri",
field=models.TextField(default=""),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2grant",
name="scope",
field=models.TextField(blank=True),
),
migrations.AddField(
model_name="oauth2grant",
name="updated",
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name="oauth2grant",
name="user",
field=models.ForeignKey(
default=0,
on_delete=django.db.models.deletion.CASCADE,
related_name="sample_identity_oauth2_provider_oauth2grant",
to=settings.AUTH_USER_MODEL,
),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2idtoken",
name="application",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL,
),
),
migrations.AddField(
model_name="oauth2idtoken",
name="created",
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2idtoken",
name="expires",
field=models.DateTimeField(default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2idtoken",
name="jti",
field=models.UUIDField(default=uuid.uuid4, editable=False, unique=True, verbose_name="JWT Token ID"),
),
migrations.AddField(
model_name="oauth2idtoken",
name="scope",
field=models.TextField(blank=True),
),
migrations.AddField(
model_name="oauth2idtoken",
name="updated",
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name="oauth2idtoken",
name="user",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="sample_identity_oauth2_provider_oauth2idtoken",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AddField(
model_name="oauth2refreshtoken",
name="access_token",
field=models.OneToOneField(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="refresh_token",
to=settings.OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL,
),
),
migrations.AddField(
model_name="oauth2refreshtoken",
name="application",
field=models.ForeignKey(
default=0, on_delete=django.db.models.deletion.CASCADE, to="sample_identity_oauth2_provider.oauth2application"
),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2refreshtoken",
name="created",
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2refreshtoken",
name="revoked",
field=models.DateTimeField(null=True),
),
migrations.AddField(
model_name="oauth2refreshtoken",
name="token",
field=models.CharField(default="", max_length=255),
preserve_default=False,
),
migrations.AddField(
model_name="oauth2refreshtoken",
name="updated",
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name="oauth2refreshtoken",
name="user",
field=models.ForeignKey(
default=0,
on_delete=django.db.models.deletion.CASCADE,
related_name="sample_identity_oauth2_provider_oauth2refreshtoken",
to=settings.AUTH_USER_MODEL,
),
preserve_default=False,
),
migrations.AlterField(
model_name="oauth2accesstoken",
name="id",
field=models.BigAutoField(primary_key=True, serialize=False),
),
migrations.AlterField(
model_name="oauth2application",
name="id",
field=models.BigAutoField(primary_key=True, serialize=False),
),
migrations.AlterField(
model_name="oauth2grant",
name="id",
field=models.BigAutoField(primary_key=True, serialize=False),
),
migrations.AlterField(
model_name="oauth2idtoken",
name="id",
field=models.BigAutoField(primary_key=True, serialize=False),
),
migrations.AlterField(
model_name="oauth2refreshtoken",
name="id",
field=models.BigAutoField(primary_key=True, serialize=False),
),
migrations.AlterUniqueTogether(
name="oauth2refreshtoken",
unique_together={("token", "revoked")},
),
migrations.AlterModelTable(
name="oauth2idtoken",
table="oauth2_provider_idtoken__custom",
),
] db_router.pyclass DisableOauth2ProviderMigrationsRouter:
def db_for_read(self, model, **hints):
return None
def db_for_write(self, model, **hints):
return None
def allow_relation(self, obj1, obj2, **hints):
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
if app_label == "oauth2_provider":
return False
return None |
On version run_before = [
('oauth2_provider', '0001_initial'),
] to the Application model swap migration, and then specifying the new model with |
Managed to get it working as per @michaeljaszczuk's suggestion above #634 (comment). Tried to get it working on a DB that wasn't empty to save data transfer but required empty DB to work properly. |
For anyone that's starting from a new app or haven't ran any migration for this app yet and want to swap models, here's the hacky way which works for me:
run_before = [
("oauth2_provider", "0001_initial"),
]
I have encountered all of the errors mentioned above and followed the fixes mentioned but nothing worked for me. I'm using version 2.2.0 btw. |
Also stumbled upon this and the only think that worked was the solution in #634 (comment) |
I got around this. Step 1: Setting the related fields to non (temporarily) allows the migration to work without E304 and the migration succeded without having the other error I was getting: "[...]token was declared with a lazy reference to 'myappname.accesstoken', but app 'myappname' isn't installed". Step 2: Before creating the final migration, change the settings file to swap the models. Note that I had to add values pointing As has been noted elsewhere, squashmigrations is not going to work because between these 2 migrations, you have to change the settings file. As far as I know that can't be deployed as a migration. |
Hi! I faced every issue that is explained here and after struggling for some hours I found a potential solid solution.
Well, that's good! Now, on the migration where your models are created I did not need the "run_first" statement but to fulfill the "dependencies". This is my migration (everything is needed because of the foreign keys between models) `from django.conf import settings class Migration(migrations.Migration):
` First step: accomplished. The second step brings us to the "settings.py" file. """THIS IS WHAT IS GOING TO MAP YOUR MODEL WITH THE MANAGER AND ENABLE IT INSTEAD OF CALLING THE ORIGINAL APP PLUS YOU WON'T NEED TO REGISTER your_app.Application, your_app.AccessToken, ... ON THE ADMIN The "bug" here is that the package is retrieving the modules like this: Step 3: python manage.py runserver and enjoy happy coding :) |
If someone still are having trouble after do that, consider deleting the migrations in the virtual enviroment of the oauth2 package |
ValueError: The field oauth2_provider.AccessToken.application was declared with a lazy reference to 'custom_oauth.application', but app 'custom_oauth' isn't installed. Giving above errors |
Hey guys,
So I'm swapping the OAuth models on an application that is already live. All sorts of nice things there, but I'm getting around. I have however 2 comments
I haven't found much documentation on this subject. I think it's important to mention that since multiple models are linked together, it's a good idea to swap them all if you start to swap one. I started with just changing
AccessToken
but it created all sorts of complexitiesMore importantly, I think for a brand new application, it's not possible to swap the models anymore. Indeed, with the new 1.1 datamodel,
AccessToken
referencesRefreshToken
throughsource_refresh_token
andRefreshToken
referencesAcessToken
throughaccess_token
. In your app this is ok because this is done over a few migration that creates the 2 tables with only one FK, and then add the second FK afterwards.But on new applications that try to swap the model, it will try to create the full table in one go and fail. I had to manually hack the migration and split the table creation manually.
--> I don't have a great solution for you, but tables that cross references themselves cyclically is bad news I guess
The text was updated successfully, but these errors were encountered: