-
Notifications
You must be signed in to change notification settings - Fork 540
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
Write Promoted Information to New Models #23216
base: master
Are you sure you want to change the base?
Write Promoted Information to New Models #23216
Conversation
c1be62e
to
5f49b6e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good so far. A lot to review so still need some more time to get through it. Initial thoughts are two fold:
-
anything you can do to reduce the diff size would be great. I've added comments for specifics
-
Can you add some logic to ensure we are no longer writing to the PromotedAddon or PromotedAddonVersion models? The important part is there should be 2 tests both trying to create or update the deprecated models and those tests should expect this is not possible.
src/olympia/promoted/migrations/0024_alter_promotedaddon_group_id_and_more.py
Show resolved
Hide resolved
src/olympia/promoted/migrations/0024_alter_promotedaddon_group_id_and_more.py
Outdated
Show resolved
Hide resolved
6d0db22
to
a529ac9
Compare
This comment was marked as resolved.
This comment was marked as resolved.
1a6aec4
to
adf1bbe
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you remove the help_text update and the manual migration, that makes this PR a lot easier to ship since it will no longer contain any migrations. The way I see it, even if you modify the choices removing NOT_PROMOTED it's not strictly necessary to remove that promoted group since every where it is used, it is evaluated as Falsy anyhow.
That would make it a lot easier for me to say yes on this PR, especially since it may need to go out in a cherry pick anyway.
I can understand the non-removal of the |
f7de029
to
8e7f4b6
Compare
8e7f4b6
to
1060c73
Compare
@@ -9,14 +9,18 @@ | |||
from django.utils.safestring import mark_safe | |||
from django.utils.translation import gettext | |||
|
|||
from olympia import promoted | |||
from olympia import hero, promoted | |||
from olympia.addons.models import Addon | |||
from olympia.amo.admin import AMOModelAdmin | |||
from olympia.amo.templatetags.jinja_helpers import vite_asset | |||
from olympia.discovery.models import DiscoveryItem |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is all of this really necessary for this PR? From what I can see this is a convenience? Or am I missing something? If, so I'd cut the 200 lines from the diff.
I'm really not trying to be a kill joy, just we have had several bugs during the roll out of this feature and the smaller the PR the less likely it is something goes wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's not really a way to avoid this that I can think of --
- There needs to be an admin page to write to
PromotedAddonPromotion
in stead ofPromotedAddon
PromotedAddonAdmin
includes the use ofPrimaryHeroInline
PrimaryHeroInline
cannot be used without the direct FKPromotedAddon
provided
It feels a bit cornered.
promoted_addon = models.OneToOneField( | ||
PromotedAddon, on_delete=models.CASCADE, null=False | ||
|
||
addon = models.ForeignKey(Addon, on_delete=models.CASCADE, null=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern with this is that we no longer have a built in way to ensure that a PrimaryHero
actually relates to an existing PromotedAddonPromotion
.
Even considering the current approach, we would have multiple PrimaryHero
instances per addon right, one per group. If that is the case, and the code relying on PrimaryHero
is already supporting multiple instances per addon, then why not just directly FK to PromotedAddonPromotion
. That seems like the more obvious path.. did you try that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking through and walking through the code.. couldn't we get the promoted_groups from the addon? We just need a 1:1 from PrimaryHero:Addon and then we can go self.addon.promoted_groups() to get the promoted groups. We can check if any promoted groups for that addon have can_primary_hero
with the query manager you made? Would that not work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then why not just directly FK to PromotedAddonPromotion. That seems like the more obvious path.. did you try that?
It is, but we can't because of the multiple application_id
. No single PromotedAddonPromotion represents an entire promotion.
That, and PromotedAddon
was set to NOT_PROMOTED
to disable promotion, so that the PrimaryHero could still exist and be tied to the promotion, but not be active. If we tied it to a deletable PromotedAddonPromotion
, it would cascade.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking through and walking through the code.. couldn't we get the promoted_groups from the addon? We just need a 1:1 from PrimaryHero:Addon and then we can go self.addon.promoted_groups() to get the promoted groups. We can check if any promoted groups for that addon have can_primary_hero with the query manager you made? Would that not work?
Hmm.. If we want just one PrimaryHeros on an add-on basis rather than per-promotion basis, that may work. This check is still done on a per-group basis for the addon via the all_applications_for()
call.
However, that logic also applies to the previous state -- the PrimaryHero
was tied to PromotedAddon
, and not to the add-on itself, when addon.promoted
could have been used instead, and they could have avoided some of the headache. So why wasn't it?
Now that you're scrutinizing it, I think this detail might depend on the use-case.
There also might be a separate issue here. PromotedAddon
, on deletion, would have deleted the PrimaryHero
... Since we represent deactivation with deletion rather than setting the group to NOT_PROMOTED
, the PrimaryHero would remain.
Hero = apps.get_model('hero', 'PrimaryHero') | ||
for hero in Hero.objects.all(): | ||
hero.addon = hero.promoted_addon.addon | ||
hero.promoted_group = PromotedGroup.objects.get(group_id=hero.promoted_addon.group.id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hero.promoted_addon.group_id
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is accessing the old class PromotedClass
, where its structured asgroup.id
), | ||
migrations.AddField( | ||
model_name='primaryhero', | ||
name='addon', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applying hero.0020_primaryhero_addon_primaryhero_promoted_group...Traceback (most recent call last):
File "/data/olympia/deps/lib/python3.12/site-packages/django/db/backends/utils.py", line 89, in _execute
return self.cursor.execute(sql, params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/data/olympia/deps/lib/python3.12/site-packages/django/db/backends/mysql/base.py", line 75, in execute
return self.cursor.execute(query, args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/data/olympia/deps/lib/python3.12/site-packages/MySQLdb/cursors.py", line 179, in execute
res = self._query(mogrified_query)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/data/olympia/deps/lib/python3.12/site-packages/MySQLdb/cursors.py", line 330, in _query
db.query(q)
File "/data/olympia/deps/lib/python3.12/site-packages/MySQLdb/connections.py", line 280, in query
_mysql.connection.query(self, query)
MySQLdb.OperationalError: (1060, "Duplicate column name 'addon_id'")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That might have been a fluke. I will try running against a clean master.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, that's a weird one. Yeah, I never encountered that one.
return qs | ||
|
||
|
||
class PromotedAddonAdmin(AMOModelAdmin): | ||
class PromotedAddonPromotionAdmin(AMOModelAdmin): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have lost the ability to see PrimaryHero relations in promotion edit page. Additionally we have no way of sorting/filtering for all promotions related to a single addon.
Try adding
search_fields = ('addon__guid', 'addon__slug')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have lost the ability to see PrimaryHero relations in promotion edit page.
Yes, with the loss of the direct FK Django inlines don't support inlines that don't have one (this is also what I have a feeling this is about), which is why PrimaryHeroInline
was turned into PrimaryHeroAdmin
.
Fixes: mozilla/addons#15476
Description
Changes promoted group information to be written directly to the new models rather than synced from
PromotedAddon
andPromotedApproval
. This mainly includes:PrimaryHero
'spromoted_addon
OneToOneField
to twoForeignKeys
,addon
andpromoted_group
.i.
promoted_group
allowsnull=True
to represent theNOT_PROMOTED
state. Its approval (i.e existingPromotedAddonPromotion
of thataddon
andpromoted_group
) is implicitly checked via theapproved_applications_for()
call.PromotedAddonAdmin
intoPromotedAddonPromotionAdmin
andPrimaryHeroAdmin
-- splinteringPrimaryHeroInline
off due to loss of direct FK.PromotedAddonPromotion
s &PromotedAddonVersion
s.Testing
PromotedAddonPromotion
PromotedAddonPromotion
for a group and application via the admin panel.PromotedAddonVersion
).Ex. Deleting






PromotedAddonPromotion
s:And recreating one:
PrimaryHero
PrimaryHero
through the primary hero admin page.PrimaryHero
should be created or updated as expected, just as it behaved as an Inline.Checklist
#ISSUENUM
at the top of your PR to an existing open issue in the mozilla/addons repository.