Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions backend/src/baserow/contrib/database/fields/field_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -910,16 +910,29 @@ def prepare_value_for_db(self, instance, value):
return value

def get_serializer_field(self, instance, **kwargs):
return serializers.IntegerField(
**{
"required": False,
"allow_null": False,
"min_value": 0,
"default": 0,
"max_value": instance.max_value,
**kwargs,
}
)
required = kwargs.get("required", False)
field_kwargs = {
"required": False,
"allow_null": False,
"min_value": 0,
"default": 0,
"max_value": instance.max_value,
**kwargs,
}
if required:
field_kwargs.pop("default", None)
validators = field_kwargs.get("validators", [])
validators.append(self._rating_required_validator)
field_kwargs["validators"] = validators
return serializers.IntegerField(**field_kwargs)

@staticmethod
def _rating_required_validator(value):
if value == 0:
raise ValidationError(
"This field is required.",
code="required",
)

def force_same_type_alter_column(self, from_field, to_field):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2995,6 +2995,89 @@ def test_submit_form_view_for_required_number_field_with_0(api_client, data_fixt
}


@pytest.mark.django_db
def test_submit_form_view_for_required_rating_field_with_0(api_client, data_fixture):
user, token = data_fixture.create_user_and_token()
table = data_fixture.create_database_table(user=user)
form = data_fixture.create_form_view(
table=table,
submit_action_message="Test",
submit_action_redirect_url="https://baserow.io",
)
rating_field = data_fixture.create_rating_field(table=table)
data_fixture.create_form_view_field_option(
form, rating_field, required=True, enabled=True, order=2
)

url = reverse("api:database:views:form:submit", kwargs={"slug": form.slug})

# Submitting 0 should fail because 0 means "no rating" for a required rating field
response = api_client.post(
url,
{f"field_{rating_field.id}": 0},
format="json",
HTTP_AUTHORIZATION=f"JWT {token}",
)
assert response.status_code == HTTP_400_BAD_REQUEST
response_json = response.json()
assert response_json["detail"][f"field_{rating_field.id}"][0]["code"] == "required"

# Submitting a valid rating should succeed
response = api_client.post(
url,
{f"field_{rating_field.id}": 3},
format="json",
HTTP_AUTHORIZATION=f"JWT {token}",
)
assert response.status_code == HTTP_200_OK
response_json = response.json()
assert response_json == {
"row_id": AnyInt(),
"submit_action": "MESSAGE",
"submit_action_message": "Test",
"submit_action_redirect_url": "https://baserow.io",
}


@pytest.mark.django_db
def test_submit_form_view_for_non_required_rating_field_with_0(
api_client, data_fixture
):
"""
Submitting 0 should succeed when the rating field is not required.
"""

user, token = data_fixture.create_user_and_token()
table = data_fixture.create_database_table(user=user)
form = data_fixture.create_form_view(
table=table,
submit_action_message="Test",
submit_action_redirect_url="https://baserow.io",
)
rating_field = data_fixture.create_rating_field(table=table)
data_fixture.create_form_view_field_option(
form, rating_field, required=False, enabled=True, order=2
)

url = reverse("api:database:views:form:submit", kwargs={"slug": form.slug})

response = api_client.post(
url,
{f"field_{rating_field.id}": 0},
format="json",
HTTP_AUTHORIZATION=f"JWT {token}",
)

assert response.status_code == HTTP_200_OK
response_json = response.json()
assert response_json == {
"row_id": AnyInt(),
"submit_action": "MESSAGE",
"submit_action_message": "Test",
"submit_action_redirect_url": "https://baserow.io",
}


@pytest.mark.django_db
def test_upload_file_view(api_client, data_fixture, tmpdir):
user, token = data_fixture.create_user_and_token(
Expand Down
9 changes: 9 additions & 0 deletions changelog/entries/unreleased/bug/4985_rating_field_0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "bug",
"message": "Rating field now doesn't accept 0 as a valid value in form views when the field is required",
"issue_origin": "github",
"issue_number": 4985,
"domain": "database",
"bullet_points": [],
"created_at": "2026-03-30"
}
4 changes: 2 additions & 2 deletions web-frontend/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ test *ARGS:
ci-test:
yarn test:coverage

# Update Jest snapshots
# Update Vitest snapshots
[group('3 - testing')]
update-snapshots:
yarn test --update
EXTRA_VITEST_PARAMS="--update" yarn test

# =============================================================================
# Development
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export default {
position: 'right',
fromField: this.fromField,
undoRedoActionGroupId: this.actionGroupId,
visible: true,
})
this.onDuplicationEnd()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export default {
position: this.position,
fromField: this.fromField,
undoRedoActionGroupId,
visible: true, // when inserting a new field, it should always be visible
})
},
toggle(ref, position) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
@update="update"
/>
</div>
<div v-show="touched && !isValid()" class="error">
{{ getError() }}
</div>
</div>
</template>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
<div class="grid-view__group" v-bind="$attrs">
<div class="grid-view__group-cell">
<div class="grid-view__group-value">
<component :is="groupByComponent" :field="field" :value="value" />
<component
:is="groupByComponent"
v-if="groupByComponent"
:field="field"
:value="value"
/>
</div>
<div v-if="count > 0" class="grid-view__group-count">
{{ count }}
Expand All @@ -19,8 +24,8 @@ export default {
type: Object,
required: true,
},
field: {
type: Object,
allFieldsInTable: {
type: Array,
required: true,
},
value: {
Expand All @@ -33,9 +38,24 @@ export default {
},
},
computed: {
field() {
return this.getField(this.allFieldsInTable, this.groupBy)
},
groupByComponent() {
const fieldType = this.$registry.get('field', this.field.type)
return fieldType.getGroupByComponent(this.field)
if (!this.field) {
return null
}
return this.getGroupByComponent(this.field, this)
},
},
methods: {
getField(allFieldsInTable, groupBy) {
const field = allFieldsInTable.find((f) => f.id === groupBy.field)
return field
},
getGroupByComponent(field, parent) {
const fieldType = parent.$registry.get('field', field.type)
return fieldType.getGroupByComponent(field)
},
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
>
<GridViewGroup
:group-by="groupBy"
:field="getGroupByField(groupBy)"
:all-fields-in-table="allFieldsInTable"
:value="groupSpan.value"
:count="groupSpan.count"
></GridViewGroup>
Expand All @@ -40,6 +40,14 @@ export default {
components: { GridViewGroup },
mixins: [gridViewHelpers],
props: {
/**
* All the fields in the table, regardless of the visibility, or whether they
* should be rendered.
*/
allFieldsInTable: {
type: Array,
required: true,
},
groupByValueSets: {
type: Array,
required: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<template>
<div class="grid-view__head">
<div
v-for="{ groupBy, field } in activeGroupBysWithFields"
v-for="groupBy in includeGroupBy ? activeGroupBys : []"
:key="'field-group-' + groupBy.field"
class="grid-view__head-group"
:style="{ width: groupBy.width + 'px' }"
:set="field = $options.methods.getField(allFieldsInTable, groupBy)"
>
<div class="grid-view__group-cell">
<div v-if="field" class="grid-view__group-cell">
<div class="grid-view__group-name">
{{ field.name }}
</div>
Expand Down Expand Up @@ -150,15 +151,6 @@ export default {
},
},
emits: ['dragging', 'field-created', 'refresh'],
computed: {
activeGroupBysWithFields() {
if (!this.includeGroupBy) return []
return this.activeGroupBys.map((groupBy) => ({
groupBy,
field: this.getGroupByField(groupBy),
}))
},
},
methods: {
/**
* After newField is created pressing "insert left" or "insert right" button,
Expand All @@ -171,6 +163,7 @@ export default {
newField,
fromField,
undoRedoActionGroupId = null,
visible = null,
}) {
try {
await this.$store.dispatch(
Expand All @@ -181,6 +174,7 @@ export default {
fromField,
undoRedoActionGroupId,
readOnly: this.readOnly,
visible,
}
)
} catch (error) {
Expand All @@ -200,6 +194,10 @@ export default {
onShownCreateFieldContext() {
this.$refs.createFieldContext.showFieldTypesDropdown(this.$el)
},
getField(allFieldsInTable, groupBy) {
const field = allFieldsInTable.find((f) => f.id === groupBy.field)
return field
},
},
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
</template>

<script>
import { mapGetters } from 'vuex'
import { filterHiddenFieldsFunction } from '@baserow/modules/database/utils/view'
import { notifyIf } from '@baserow/modules/core/utils/error'
import ViewFieldsContext from '@baserow/modules/database/components/view/ViewFieldsContext'

Expand Down Expand Up @@ -60,29 +60,15 @@ export default {
},
computed: {
hiddenFields() {
return this.fields.filter((field) => {
const exists = Object.prototype.hasOwnProperty.call(
this.fieldOptions,
field.id
)
return !exists || (exists && this.fieldOptions[field.id].hidden)
})
const isFieldHidden = filterHiddenFieldsFunction(this.fieldOptions)
return this.fields.filter((field) => isFieldHidden(field))
},
fieldOptions() {
return this.$store.getters[
this.storePrefix + 'view/grid/getAllFieldOptions'
]
},
},
/*beforeCreate() {
this.$options.computed = {
...(this.$options.computed || {}),
...mapGetters({
fieldOptions:
this.$options.propsData.storePrefix + 'view/grid/getAllFieldOptions',
}),
}
},*/
methods: {
async updateAllFieldOptions({ newFieldOptions, oldFieldOptions }) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
></GridViewPlaceholder>
<GridViewGroups
v-if="includeGroupBy && activeGroupBys.length > 0"
:all-fields-in-table="allFieldsInTable"
:group-by-value-sets="groupByValueSets"
:store-prefix="storePrefix"
></GridViewGroups>
Expand Down Expand Up @@ -422,7 +423,12 @@ export default {
return false
}
return groupBys.slice(0, groupByIndex + 1).every((groupBy) => {
const groupByField = this.getGroupByField(groupBy)
const groupByField = this.allFieldsInTable.find(
(f) => f.id === groupBy.field
)
if (!groupByField) {
return false
}
const groupByFieldType = this.$registry.get(
'field',
groupByField.type
Expand All @@ -444,9 +450,10 @@ export default {
(metadata[`field_${groupBy.field}`] || []).find((entry) => {
const groupByFields = groupBys
.slice(0, groupByIndex + 1)
.map((groupBy) => {
return this.getGroupByField(groupBy)
})
.map((groupBy) =>
this.allFieldsInTable.find((f) => f.id === groupBy.field)
)
.filter(Boolean)
return fieldValuesAreEqualInObjects(
groupByFields,
this.$registry,
Expand Down
Loading
Loading